blob: bb49c2d7f839d2377c6a9eb2f83d9c0b6c84313c [file] [log] [blame]
// Copyright 2018 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/overnet/overnetstack/mdns.h"
#include <fbl/ref_counted.h>
#include <fuchsia/mdns/cpp/fidl.h>
#include "garnet/public/lib/fostr/fidl/fuchsia/mdns/formatting.h"
#include "src/connectivity/overnet/lib/labels/node_id.h"
#include "src/connectivity/overnet/overnetstack/fuchsia_port.h"
namespace overnetstack {
static const char* kServiceName = "_temp_overnet._udp.";
static fuchsia::mdns::ControllerPtr Connect(
sys::ComponentContext* component_context, const char* why) {
auto svc = component_context->svc()->Connect<fuchsia::mdns::Controller>();
svc.set_error_handler([why](zx_status_t status) {
OVERNET_TRACE(ERROR) << why
<< " mdns failure: " << zx_status_get_string(status);
});
return svc;
}
class MdnsIntroducer::Impl : public fbl::RefCounted<MdnsIntroducer>,
public fuchsia::mdns::ServiceSubscriber {
public:
Impl(UdpNub* nub) : nub_(nub), subscriber_binding_(this) {}
void Begin(sys::ComponentContext* component_context) {
std::cerr << "Querying mDNS for overnet services [" << kServiceName
<< "]\n";
auto svc = Connect(component_context, "Introducer");
fidl::InterfaceHandle<fuchsia::mdns::ServiceSubscriber> subscriber_handle;
subscriber_binding_.Bind(subscriber_handle.NewRequest());
subscriber_binding_.set_error_handler([this](zx_status_t status) {
subscriber_binding_.set_error_handler(nullptr);
subscriber_binding_.Unbind();
});
svc->SubscribeToService(kServiceName, std::move(subscriber_handle));
}
private:
struct ServiceInstance {
ServiceInstance(std::vector<std::string> t,
std::vector<fuchsia::netstack::SocketAddress> a)
: text(std::move(t)), addresses(std::move(a)) {}
std::vector<std::string> text;
std::vector<fuchsia::netstack::SocketAddress> addresses;
};
using ServiceMap = std::map<overnet::NodeId, ServiceInstance>;
void HandleDiscoverOrUpdate(const fuchsia::mdns::ServiceInstance& svc,
bool update) {
if (svc.service_name != kServiceName) {
std::cout << "Unexpected service name (ignored): " << svc.service_name
<< "\n";
return;
}
auto parsed_instance_name = overnet::NodeId::FromString(svc.instance_name);
if (parsed_instance_name.is_error()) {
std::cout << "Failed to parse instance name: "
<< parsed_instance_name.AsStatus() << "\n";
return;
}
auto instance_id = *parsed_instance_name.get();
auto it = service_map_.find(instance_id);
if ((it != service_map_.end()) != update) {
if (update) {
std::cout << "WARNING: Update for unknown instance " << instance_id
<< "; ignoring\n";
} else {
std::cout << "WARNING: Discovery of known instance " << instance_id
<< "; ignoring\n";
}
return;
}
std::vector<fuchsia::netstack::SocketAddress> addresses;
if (svc.v4_address) {
addresses.emplace_back();
auto result =
overnet::Status::FromZx(svc.v4_address->Clone(&addresses.back()));
if (result.is_error()) {
std::cout << "Failed to clone v4_address: " << result << "\n";
addresses.pop_back();
}
}
if (svc.v6_address) {
addresses.emplace_back();
auto result =
overnet::Status::FromZx(svc.v6_address->Clone(&addresses.back()));
if (result.is_error()) {
std::cout << "Failed to clone v6_address: " << result << "\n";
addresses.pop_back();
}
}
std::vector<std::string> text;
for (const auto& line : svc.text) {
text.push_back(line);
}
if (it == service_map_.end()) {
NewConnection(instance_id, addresses);
service_map_.emplace(
std::piecewise_construct, std::forward_as_tuple(instance_id),
std::forward_as_tuple(std::move(text), std::move(addresses)));
} else if (it->second.addresses != addresses) {
NewConnection(instance_id, addresses);
it->second.addresses = std::move(addresses);
}
}
void NewConnection(
overnet::NodeId node_id,
const std::vector<fuchsia::netstack::SocketAddress>& addresses) {
for (const auto& addr : addresses) {
auto status = ToUdpAddr(addr).Then(
[node_id, nub = nub_](const overnet::IpAddr& addr) {
std::cerr << "Initiating connection to: " << node_id << " at "
<< addr << "\n";
nub->Initiate(addr, node_id);
return overnet::Status::Ok();
});
if (status.is_error()) {
std::cerr << "Failed to initiate connection: " << status << "\n";
}
}
}
static overnet::StatusOr<overnet::IpAddr> ToUdpAddr(
const fuchsia::netstack::SocketAddress& sock_addr) {
const fuchsia::net::IpAddress& net_addr = sock_addr.addr;
overnet::IpAddr udp_addr;
memset(&udp_addr, 0, sizeof(udp_addr));
switch (net_addr.Which()) {
case fuchsia::net::IpAddress::Tag::Invalid:
return overnet::Status(overnet::StatusCode::INVALID_ARGUMENT,
"unknown address type");
case fuchsia::net::IpAddress::Tag::kIpv4:
if (!net_addr.is_ipv4()) {
return overnet::Status(overnet::StatusCode::INVALID_ARGUMENT,
"bad ipv4 address");
}
udp_addr.ipv4.sin_family = AF_INET;
udp_addr.ipv4.sin_port = htons(sock_addr.port);
memcpy(&udp_addr.ipv4.sin_addr, net_addr.ipv4().addr.data(),
sizeof(udp_addr.ipv4.sin_addr));
return udp_addr;
case fuchsia::net::IpAddress::Tag::kIpv6:
if (!net_addr.is_ipv6()) {
return overnet::Status(overnet::StatusCode::INVALID_ARGUMENT,
"bad ipv6 address");
}
udp_addr.ipv6.sin6_family = AF_INET6;
udp_addr.ipv6.sin6_port = htons(sock_addr.port);
memcpy(&udp_addr.ipv6.sin6_addr, net_addr.ipv6().addr.data(),
sizeof(udp_addr.ipv6.sin6_addr));
return udp_addr;
}
return overnet::Status(overnet::StatusCode::INVALID_ARGUMENT,
"bad address family");
}
// fuchsia::mdns::ServiceSubscriber implementation.
void InstanceDiscovered(fuchsia::mdns::ServiceInstance instance,
InstanceDiscoveredCallback callback) {
HandleDiscoverOrUpdate(instance, false);
callback();
}
void InstanceChanged(fuchsia::mdns::ServiceInstance instance,
InstanceChangedCallback callback) {
HandleDiscoverOrUpdate(instance, true);
callback();
}
void InstanceLost(std::string service_name, std::string instance_name,
InstanceLostCallback callback) {
callback();
}
UdpNub* const nub_;
fidl::Binding<fuchsia::mdns::ServiceSubscriber> subscriber_binding_;
ServiceMap service_map_;
};
MdnsIntroducer::MdnsIntroducer(OvernetApp* app, UdpNub* udp_nub)
: app_(app), udp_nub_(udp_nub) {}
overnet::Status MdnsIntroducer::Start() {
auto impl = fbl::MakeRefCounted<Impl>(udp_nub_);
impl_ = std::move(impl);
impl_->Begin(app_->component_context());
return overnet::Status::Ok();
}
MdnsIntroducer::~MdnsIntroducer() {}
class MdnsAdvertisement::Impl : public fuchsia::mdns::Responder {
public:
Impl(sys::ComponentContext* component_context, UdpNub* nub)
: controller_(Connect(component_context, "Advertisement")),
node_id_(nub->node_id()),
binding_(this),
port_(nub->port()) {
std::cerr << "Requesting mDNS advertisement for " << node_id_ << " on port "
<< nub->port() << "\n";
controller_->PublishServiceInstance(
kServiceName, node_id_.ToString(), true, binding_.NewBinding(),
[node_id = node_id_, port = port_](fuchsia::mdns::Result result) {
std::cout << "Advertising " << node_id << " on port " << port
<< " via mdns gets: " << result << "\n";
});
}
~Impl() = default;
private:
// fuchsia::mdns::Responder implementation.
void GetPublication(bool query, fidl::StringPtr subtype,
GetPublicationCallback callback) {
callback(subtype->empty() ? std::make_unique<fuchsia::mdns::Publication>(
fuchsia::mdns::Publication{.port = port_})
: nullptr);
}
const fuchsia::mdns::ControllerPtr controller_;
const overnet::NodeId node_id_;
fidl::Binding<fuchsia::mdns::Responder> binding_;
const uint16_t port_;
};
MdnsAdvertisement::MdnsAdvertisement(OvernetApp* app, UdpNub* udp_nub)
: app_(app), udp_nub_(udp_nub) {}
overnet::Status MdnsAdvertisement::Start() {
impl_.reset(new Impl(app_->component_context(), udp_nub_));
return overnet::Status::Ok();
}
MdnsAdvertisement::~MdnsAdvertisement() {}
} // namespace overnetstack