blob: 707ab6f05fdb6167e043a02ac904f8827cf0f7d3 [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/net/mdns/cpp/fidl.h>
#include "garnet/public/lib/fostr/fidl/fuchsia/net/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::net::mdns::SubscriberPtr ConnectToSubscriber(
sys::ComponentContext* component_context, const char* why) {
auto svc =
component_context->svc()->Connect<fuchsia::net::mdns::Subscriber>();
svc.set_error_handler([why](zx_status_t status) {
OVERNET_TRACE(ERROR) << why << " mdns subscriber failure: "
<< zx_status_get_string(status);
});
return svc;
}
static fuchsia::net::mdns::PublisherPtr ConnectToPublisher(
sys::ComponentContext* component_context, const char* why) {
auto svc = component_context->svc()->Connect<fuchsia::net::mdns::Publisher>();
svc.set_error_handler([why](zx_status_t status) {
OVERNET_TRACE(ERROR) << why << " mdns publisher failure: "
<< zx_status_get_string(status);
});
return svc;
}
class MdnsIntroducer::Impl : public fbl::RefCounted<MdnsIntroducer>,
public fuchsia::net::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 = ConnectToSubscriber(component_context, "Introducer");
fidl::InterfaceHandle<fuchsia::net::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:
void HandleDiscoverOrUpdate(const fuchsia::net::mdns::ServiceInstance& svc,
bool update) {
if (svc.service != kServiceName) {
std::cout << "Unexpected service name (ignored): " << svc.service << "\n";
return;
}
auto parsed_instance_name = overnet::NodeId::FromString(svc.instance);
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();
std::vector<overnet::IpAddr> addrs;
for (const auto& endpoint : svc.endpoints) {
auto status = ToIpAddr(endpoint);
if (status.is_error()) {
std::cout << "Failed to convert address: " << status << "\n";
} else {
addrs.emplace_back(std::move(*status));
}
}
nub_->Initiate(std::move(addrs), instance_id);
}
static overnet::StatusOr<overnet::IpAddr> ToIpAddr(
const fuchsia::net::Endpoint& endpoint) {
const fuchsia::net::IpAddress& net_addr = endpoint.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(endpoint.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(endpoint.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::net::mdns::ServiceSubscriber implementation.
void OnInstanceDiscovered(fuchsia::net::mdns::ServiceInstance instance,
OnInstanceDiscoveredCallback callback) {
HandleDiscoverOrUpdate(instance, false);
callback();
}
void OnInstanceChanged(fuchsia::net::mdns::ServiceInstance instance,
OnInstanceChangedCallback callback) {
HandleDiscoverOrUpdate(instance, true);
callback();
}
void OnInstanceLost(std::string service, std::string instance,
OnInstanceLostCallback callback) {
callback();
}
UdpNub* const nub_;
fidl::Binding<fuchsia::net::mdns::ServiceSubscriber> subscriber_binding_;
};
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::net::mdns::PublicationResponder {
public:
Impl(sys::ComponentContext* component_context, UdpNub* nub)
: publisher_(ConnectToPublisher(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";
publisher_->PublishServiceInstance(
kServiceName, node_id_.ToString(), true, binding_.NewBinding(),
[node_id = node_id_, port = port_](
fuchsia::net::mdns::Publisher_PublishServiceInstance_Result
result) {
if (result.is_err()) {
std::cout << "Advertising " << node_id << " on port " << port
<< " via mdns gets: " << result.err() << "\n";
} else {
std::cout << "Advertising " << node_id << " on port " << port
<< " via mdns succeeded\n";
}
});
}
~Impl() = default;
private:
// fuchsia::net::mdns::PublicationResponder implementation.
void OnPublication(bool query, fidl::StringPtr subtype,
OnPublicationCallback callback) {
callback(subtype->empty()
? std::make_unique<fuchsia::net::mdns::Publication>(
fuchsia::net::mdns::Publication{.port = port_})
: nullptr);
}
const fuchsia::net::mdns::PublisherPtr publisher_;
const overnet::NodeId node_id_;
fidl::Binding<fuchsia::net::mdns::PublicationResponder> 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