blob: 71f7040998ce0750331831492896b3f6512d2ab8 [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 "garnet/bin/netconnector/mdns/mdns_transceiver.h"
#include <arpa/inet.h>
#include <errno.h>
#include <sys/socket.h>
#include "garnet/bin/media/util/fidl_publisher.h"
#include "garnet/bin/netconnector/mdns/mdns_addresses.h"
#include "lib/app/fidl/application_launcher.fidl.h"
#include "lib/app/fidl/service_provider.fidl.h"
#include "lib/fidl/cpp/bindings/binding_set.h"
#include "lib/fsl/tasks/message_loop.h"
#include "lib/fxl/files/unique_fd.h"
#include "lib/fxl/logging.h"
namespace netconnector {
namespace mdns {
// static
const fxl::TimeDelta MdnsTransceiver::kMinAddressRecheckDelay =
fxl::TimeDelta::FromSeconds(1);
// static
const fxl::TimeDelta MdnsTransceiver::kMaxAddressRecheckDelay =
fxl::TimeDelta::FromSeconds(5 * 60);
MdnsTransceiver::MdnsTransceiver()
: task_runner_(fsl::MessageLoop::GetCurrent()->task_runner()),
application_context_(app::ApplicationContext::CreateFromStartupInfo()) {
netstack_ =
application_context_->ConnectToEnvironmentService<netstack::Netstack>();
}
MdnsTransceiver::~MdnsTransceiver() {}
void MdnsTransceiver::EnableInterface(const std::string& name,
sa_family_t family) {
enabled_interfaces_.emplace_back(name, family);
}
void MdnsTransceiver::Start(
const LinkChangeCallback& link_change_callback,
const InboundMessageCallback& inbound_message_callback) {
FXL_DCHECK(link_change_callback);
FXL_DCHECK(inbound_message_callback);
link_change_callback_ = link_change_callback;
inbound_message_callback_ = inbound_message_callback;
FindNewInterfaces();
}
void MdnsTransceiver::Stop() {
for (auto& interface : interfaces_) {
interface->Stop();
}
}
void MdnsTransceiver::SetHostFullName(const std::string& host_full_name) {
FXL_DCHECK(!host_full_name.empty());
host_full_name_ = host_full_name;
for (auto& i : interfaces_) {
i->SetHostFullName(host_full_name_);
}
}
bool MdnsTransceiver::InterfaceEnabled(const netstack::NetInterface* if_info) {
if ((if_info->flags & netstack::NetInterfaceFlagUp) == 0) {
return false;
}
IpAddress addr(if_info->addr.get());
if (addr.is_loopback()) {
return false;
}
if (enabled_interfaces_.empty()) {
return true;
}
for (auto& enabled_interface : enabled_interfaces_) {
if (enabled_interface.name_ == if_info->name &&
enabled_interface.family_ == addr.family()) {
return true;
}
}
return false;
}
void MdnsTransceiver::SendMessage(DnsMessage* message,
const ReplyAddress& reply_address) {
FXL_DCHECK(message);
if (reply_address.socket_address() == MdnsAddresses::kV4Multicast) {
for (auto& i : interfaces_) {
i->SendMessage(message, reply_address.socket_address());
}
return;
}
FXL_DCHECK(reply_address.interface_index() < interfaces_.size());
interfaces_[reply_address.interface_index()]->SendMessage(
message, reply_address.socket_address());
}
void MdnsTransceiver::FindNewInterfaces() {
netstack_->GetInterfaces(
[this](fidl::Array<netstack::NetInterfacePtr> interfaces) {
bool recheck_addresses = false;
bool link_change = false;
if (interfaces.size() == 0) {
recheck_addresses = true;
}
// Launch a transceiver for each new interface.
for (const auto& if_info : interfaces) {
if (if_info->addr->family ==
netstack::NetAddressFamily::UNSPECIFIED) {
recheck_addresses = true;
continue;
}
if (InterfaceEnabled(if_info.get())) {
IpAddress address(if_info->addr.get());
if (InterfaceAlreadyFound(address)) {
continue;
}
std::unique_ptr<MdnsInterfaceTransceiver> interface =
MdnsInterfaceTransceiver::Create(if_info.get(),
interfaces_.size());
if (!interface->Start(inbound_message_callback_)) {
continue;
}
if (!host_full_name_.empty()) {
interface->SetHostFullName(host_full_name_);
}
for (auto& i : interfaces_) {
if (i->name() == interface->name()) {
i->SetAlternateAddress(host_full_name_, interface->address());
interface->SetAlternateAddress(host_full_name_, i->address());
}
}
interfaces_.push_back(std::move(interface));
link_change = true;
}
}
if (recheck_addresses) {
task_runner_->PostDelayedTask([this]() { FindNewInterfaces(); },
address_recheck_delay_);
address_recheck_delay_ =
std::min(address_recheck_delay_ * kAddressRecheckDelayMultiplier,
kMaxAddressRecheckDelay);
}
if (link_change) {
FXL_DCHECK(link_change_callback_);
link_change_callback_();
}
});
}
bool MdnsTransceiver::InterfaceAlreadyFound(const IpAddress& address) {
for (auto& i : interfaces_) {
if (i->address() == address) {
return true;
}
}
return false;
}
} // namespace mdns
} // namespace netconnector