blob: 388e047140da1bc85117981a89c99a481c82ac00 [file] [log] [blame]
// Copyright 2022 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/service/agents/service_instance_resolver.h"
#include <lib/syslog/cpp/macros.h>
#include <lib/zx/time.h>
#include "src/connectivity/network/mdns/service/common/mdns_fidl_util.h"
#include "src/connectivity/network/mdns/service/common/mdns_names.h"
#include "src/connectivity/network/mdns/service/common/type_converters.h"
namespace mdns {
namespace {
constexpr zx::duration kAdditionalInterval = zx::sec(1);
constexpr uint32_t kAdditionalIntervalMultiplier = 2;
constexpr uint32_t kAdditionalMaxQueries = 3;
} // namespace
ServiceInstanceResolver::ServiceInstanceResolver(MdnsAgent::Owner* owner,
const std::string& service,
const std::string& instance, zx::time timeout,
Media media, IpVersions ip_versions,
bool include_local, bool include_local_proxies,
Mdns::ResolveServiceInstanceCallback callback)
: MdnsAgent(owner),
service_(service),
instance_name_(instance),
timeout_(timeout),
media_(media),
ip_versions_(ip_versions),
include_local_(include_local),
include_local_proxies_(include_local_proxies),
callback_(std::move(callback)),
aaaa_queried_(false) {
FX_DCHECK(callback_);
}
ServiceInstanceResolver::~ServiceInstanceResolver() {}
void ServiceInstanceResolver::Quit() {
if (callback_) {
callback_(std::move(instance_));
callback_ = nullptr;
}
MdnsAgent::Quit();
}
void ServiceInstanceResolver::EndOfMessage() {
if (!callback_) {
// This can happen when a redundant response is received after the block below runs and before
// the posted task runs, e.g. when two NICs are connected to the same LAN.
return;
}
// if srv was received but has no aaaa record, send aaaa query.
// Since nsec is not supported, the following check is intentional as
// ServiceInstanceResolver supports only v6 addresses.
if (!aaaa_queried_ && instance_.has_service() && !instance_.has_ipv6_endpoint()) {
Query(DnsType::kAaaa, target_full_name_, media_, ip_versions_, now(), kAdditionalInterval,
kAdditionalIntervalMultiplier, kAdditionalMaxQueries, true);
aaaa_queried_ = true;
return;
}
if (port_.is_valid() && instance_.has_ipv6_endpoint()) {
callback_(std::move(instance_));
callback_ = nullptr;
PostTaskForTime([this]() { RemoveSelf(); }, now());
}
}
void ServiceInstanceResolver::Start(const std::string& service_instance) {
MdnsAgent::Start(service_instance);
service_instance_ = MdnsNames::InstanceFullName(instance_name_, service_);
// Increase the chance of coalescing the queries together in one message.
auto ts = now();
Query(DnsType::kSrv, service_instance_, media_, ip_versions_, ts, kAdditionalInterval,
kAdditionalIntervalMultiplier, kAdditionalMaxQueries, true);
Query(DnsType::kTxt, service_instance_, media_, ip_versions_, ts, kAdditionalInterval,
kAdditionalIntervalMultiplier, kAdditionalMaxQueries, true);
PostTaskForTime(
[this]() {
if (callback_) {
callback_(std::move(instance_));
callback_ = nullptr;
RemoveSelf();
}
},
timeout_);
}
void ServiceInstanceResolver::ReceiveResource(const DnsResource& resource,
MdnsResourceSection section,
ReplyAddress sender_address) {
if (!sender_address.Matches(media_) || !sender_address.Matches(ip_versions_)) {
return;
}
switch (resource.type_) {
case DnsType::kSrv:
if (resource.name_.dotted_string_ == service_instance_) {
instance_.set_service(service_);
instance_.set_instance(instance_name_);
instance_.set_srv_priority(resource.srv_.priority_);
instance_.set_srv_weight(resource.srv_.weight_);
port_ = resource.srv_.port_;
target_full_name_ = resource.srv_.target_.dotted_string_;
instance_.set_target(MdnsNames::HostNameFromFullName(target_full_name_));
}
break;
case DnsType::kA:
if (resource.name_.dotted_string_ == target_full_name_) {
auto address = MdnsFidlUtil::CreateSocketAddressV4(
inet::SocketAddress(resource.a_.address_.address_, port_));
instance_.set_ipv4_endpoint(address);
if (!instance_.has_addresses()) {
instance_.set_addresses(std::vector<fuchsia::net::SocketAddress>());
}
instance_.mutable_addresses()->push_back(
fuchsia::net::SocketAddress::WithIpv4(std::move(address)));
}
break;
case DnsType::kAaaa:
if (resource.name_.dotted_string_ == target_full_name_) {
// Add scope_id only to link local addresses.
uint32_t scope_id = 0;
if (resource.aaaa_.address_.address_.is_link_local()) {
scope_id = sender_address.interface_id();
}
auto address = MdnsFidlUtil::CreateSocketAddressV6(
inet::SocketAddress(resource.aaaa_.address_.address_, port_, scope_id));
instance_.set_ipv6_endpoint(address);
if (!instance_.has_addresses()) {
instance_.set_addresses(std::vector<fuchsia::net::SocketAddress>());
}
instance_.mutable_addresses()->push_back(
fuchsia::net::SocketAddress::WithIpv6(std::move(address)));
}
break;
case DnsType::kTxt:
if (resource.name_.dotted_string_ == target_full_name_) {
instance_.set_text(fidl::To<std::vector<std::string>>(resource.txt_.strings_));
instance_.set_text_strings(fidl::Clone(resource.txt_.strings_));
}
break;
default:
break;
}
}
void ServiceInstanceResolver::OnAddLocalServiceInstance(const Mdns::ServiceInstance& instance,
bool from_proxy) {
if (!callback_) {
return;
}
if (from_proxy ? !include_local_proxies_ : !include_local_) {
return;
}
if (instance.service_name_ != service_ || instance.instance_name_ != instance_name_) {
return;
}
callback_(fidl::To<fuchsia::net::mdns::ServiceInstance>(instance));
callback_ = nullptr;
PostTaskForTime([this]() { RemoveSelf(); }, now());
}
} // namespace mdns