blob: 518c33b22abad85b6be3b503cc5b44e077380f95 [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 <fuchsia/net/mdns/cpp/fidl.h>
#include <lib/async-loop/default.h>
#include <lib/async-loop/loop.h>
#include <lib/async/default.h>
#include <poll.h>
#include <iostream>
#include <unordered_set>
#include "lib/fidl/cpp/type_converter.h"
#include "src/connectivity/network/mdns/util/formatting.h"
#include "src/connectivity/network/mdns/util/mdns_params.h"
#include "src/lib/fsl/types/type_converters.h"
#include "src/lib/syslog/cpp/logger.h"
namespace mdns {
MdnsImpl::MdnsImpl(sys::ComponentContext* component_context, MdnsParams* params,
fit::closure quit_callback)
: component_context_(component_context),
quit_callback_(std::move(quit_callback)),
responder_binding_(this),
subscriber_binding_(this) {
FX_DCHECK(component_context);
FX_DCHECK(params);
FX_DCHECK(quit_callback_);
switch (params->command_verb()) {
case MdnsParams::CommandVerb::kResolve:
Resolve(params->host_name(), params->timeout_seconds());
break;
case MdnsParams::CommandVerb::kSubscribe:
Subscribe(params->service_name());
break;
case MdnsParams::CommandVerb::kRespond:
Respond(params->service_name(), params->instance_name(), params->port(), params->announce(),
params->text());
break;
}
}
MdnsImpl::~MdnsImpl() {}
void MdnsImpl::WaitForKeystroke() {
fd_waiter_.Wait([this](zx_status_t status, uint32_t events) { HandleKeystroke(); }, 0, POLLIN);
}
void MdnsImpl::HandleKeystroke() {
int c = getc(stdin);
if (c == 27) {
quit_callback_();
}
WaitForKeystroke();
}
void MdnsImpl::Resolve(const std::string& host_name, uint32_t timeout_seconds) {
std::cout << "resolving " << host_name << "\n";
EnsureResolver();
resolver_->ResolveHostName(
host_name, timeout_seconds * 1000,
[this](fuchsia::net::Ipv4AddressPtr v4Address, fuchsia::net::Ipv6AddressPtr v6Address) {
if (v4Address) {
std::cout << "IPv4 address: " << *v4Address << "\n";
}
if (v6Address) {
std::cout << "IPv6 address: " << *v6Address << "\n";
}
if (!v4Address && !v6Address) {
std::cout << "not found\n";
}
Quit();
});
}
void MdnsImpl::Subscribe(const std::string& service_name) {
std::cout << "subscribing to service " << service_name << "\n";
std::cout << "press escape key to quit\n";
fidl::InterfaceHandle<fuchsia::net::mdns::ServiceSubscriber> subscriber_handle;
subscriber_binding_.Bind(subscriber_handle.NewRequest());
subscriber_binding_.set_error_handler([this](zx_status_t status) {
std::cout << "mDNS service disconnected from subscriber unexpectedly\n";
Quit();
});
EnsureSubscriber();
subscriber_->SubscribeToService(service_name, std::move(subscriber_handle));
WaitForKeystroke();
}
void MdnsImpl::Respond(const std::string& service_name, const std::string& instance_name,
uint16_t port, const std::vector<std::string>& announce,
const std::vector<std::string>& text) {
std::cout << "responding as instance " << instance_name << " of service " << service_name << "\n";
std::cout << "press escape key to quit\n";
fidl::InterfaceHandle<fuchsia::net::mdns::PublicationResponder> responder_handle;
responder_binding_.Bind(responder_handle.NewRequest());
responder_binding_.set_error_handler([this](zx_status_t status) {
std::cout << "mDNS service disconnected from responder unexpectedly\n";
Quit();
});
publication_port_ = port;
publication_text_ = text;
EnsurePublisher();
publisher_->PublishServiceInstance(
service_name, instance_name, true, std::move(responder_handle),
[this](fuchsia::net::mdns::Publisher_PublishServiceInstance_Result result) {
if (result.is_response()) {
std::cout << "instance successfully published\n";
} else {
switch (result.err()) {
case fuchsia::net::mdns::Error::INVALID_SERVICE_NAME:
std::cout << "ERROR: service name is invalid\n";
break;
case fuchsia::net::mdns::Error::INVALID_INSTANCE_NAME:
std::cout << "ERROR: instance name is invalid\n";
break;
case fuchsia::net::mdns::Error::ALREADY_PUBLISHED_LOCALLY:
std::cout << "ERROR: instance was already published by this host\n";
break;
case fuchsia::net::mdns::Error::ALREADY_PUBLISHED_ON_SUBNET:
std::cout << "ERROR: instance was already published by another "
"host on the subnet\n";
break;
// The default case has been deliberately omitted here so that
// this switch statement will be updated whenever the |Result|
// enum is changed.
}
Quit();
}
});
if (!announce.empty()) {
responder_binding_.events().SetSubtypes(announce);
}
WaitForKeystroke();
}
void MdnsImpl::EnsureResolver() {
if (resolver_) {
return;
}
resolver_ = component_context_->svc()->Connect<fuchsia::net::mdns::Resolver>();
resolver_.set_error_handler([this](zx_status_t status) {
std::cout << "fuchsia::net::mdns::Resolver channel disconnected unexpectedly\n";
Quit();
});
}
void MdnsImpl::EnsureSubscriber() {
if (subscriber_) {
return;
}
subscriber_ = component_context_->svc()->Connect<fuchsia::net::mdns::Subscriber>();
subscriber_.set_error_handler([this](zx_status_t status) {
std::cout << "fuchsia::net::mdns::Subscriber channel disconnected unexpectedly\n";
Quit();
});
}
void MdnsImpl::EnsurePublisher() {
if (publisher_) {
return;
}
publisher_ = component_context_->svc()->Connect<fuchsia::net::mdns::Publisher>();
publisher_.set_error_handler([this](zx_status_t status) {
std::cout << "fuchsia::net::mdns::Publisher channel disconnected unexpectedly\n";
Quit();
});
}
void MdnsImpl::Quit() {
resolver_.set_error_handler(nullptr);
publisher_.set_error_handler(nullptr);
subscriber_.set_error_handler(nullptr);
responder_binding_.set_error_handler(nullptr);
subscriber_binding_.set_error_handler(nullptr);
resolver_.Unbind();
publisher_.Unbind();
subscriber_.Unbind();
responder_binding_.Unbind();
subscriber_binding_.Unbind();
quit_callback_();
}
void MdnsImpl::OnPublication(bool query, fidl::StringPtr subtype, OnPublicationCallback callback) {
std::cout << (query ? "query" : "initial publication");
if (subtype) {
std::cout << " for subtype " << subtype;
}
std::cout << "\n";
auto publication = fuchsia::net::mdns::Publication::New();
publication->port = publication_port_;
publication->text = publication_text_;
callback(std::move(publication));
}
void MdnsImpl::OnInstanceDiscovered(fuchsia::net::mdns::ServiceInstance instance,
OnInstanceDiscoveredCallback callback) {
std::cout << "discovered:" << fostr::Indent << fostr::NewLine << instance << fostr::Outdent
<< "\n";
callback();
}
void MdnsImpl::OnInstanceChanged(fuchsia::net::mdns::ServiceInstance instance,
OnInstanceChangedCallback callback) {
std::cout << "changed:" << fostr::Indent << fostr::NewLine << instance << fostr::Outdent << "\n";
callback();
}
void MdnsImpl::OnInstanceLost(std::string service_name, std::string instance_name,
OnInstanceLostCallback callback) {
std::cout << "lost:" << fostr::Indent << fostr::NewLine << service_name << " " << instance_name
<< fostr::Outdent << "\n";
callback();
}
} // namespace mdns