blob: 42a466e3901cddbef860b035d222d1057e11ce38 [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/service/agents/resource_renewer.h"
#include <lib/syslog/cpp/macros.h>
#include <lib/zx/clock.h>
namespace mdns {
namespace {
Media Union(Media a, Media b) {
if (a == b) {
return a;
}
return Media::kBoth;
}
IpVersions Union(IpVersions a, IpVersions b) {
if (a == b) {
return a;
}
return IpVersions::kBoth;
}
} // namespace
ResourceRenewer::ResourceRenewer(MdnsAgent::Owner* owner) : MdnsAgent(owner) {}
ResourceRenewer::~ResourceRenewer() { FX_DCHECK(entries_.size() == schedule_.size()); }
void ResourceRenewer::Renew(const DnsResource& resource, Media media, IpVersions ip_versions) {
FX_DCHECK(resource.time_to_live_ != 0);
Query(resource.type_, resource.name_.dotted_string_, media, ip_versions,
now() + zx::msec(resource.time_to_live_ * kFirstQueryPerThousand),
zx::msec(resource.time_to_live_ * kQueryIntervalPerThousand), 1, kQueriesToAttempt);
}
void ResourceRenewer::Query(DnsType type, const std::string& name, Media media,
IpVersions ip_versions, zx::time initial_query_time,
zx::duration interval, uint32_t interval_multiplier,
uint32_t max_queries, bool request_unicast_response) {
auto entry = std::make_unique<Entry>(name, type, media, ip_versions, request_unicast_response);
auto iter = entries_.find(entry);
if (iter == entries_.end()) {
entry->SetFirstQuery(initial_query_time, interval, interval_multiplier, max_queries);
Schedule(entry.get());
if (entry.get() == schedule_.top()) {
PostTaskForTime([this]() { SendRenewals(); }, entry->schedule_time_);
}
entries_.insert(std::move(entry));
} else {
(*iter)->SetFirstQuery(initial_query_time, interval, interval_multiplier, max_queries);
(*iter)->delete_ = false;
(*iter)->media_ = Union((*iter)->media_, media);
(*iter)->ip_versions_ = Union((*iter)->ip_versions_, ip_versions);
}
}
void ResourceRenewer::ReceiveResource(const DnsResource& resource, MdnsResourceSection section,
ReplyAddress sender_address) {
FX_DCHECK(section != MdnsResourceSection::kExpired);
// |key| is just used as a key, so media and ip_versions are irrelevant.
auto key = std::make_unique<Entry>(resource.name_.dotted_string_, resource.type_, Media::kBoth,
IpVersions::kBoth, false);
auto iter = entries_.find(key);
if (iter != entries_.end()) {
if (sender_address.Matches((*iter)->media_) && sender_address.Matches((*iter)->ip_versions_)) {
(*iter)->delete_ = true;
}
}
}
void ResourceRenewer::Quit() {
// This is never called.
FX_DCHECK(false);
}
void ResourceRenewer::SendRenewals() {
zx::time now = this->now();
// We add |kFudgeFactor| when we compare here to increase the chance of coalescing queries that
// are scheduled close together. Queries may be sent as much as |kFudgeFactor| early.
while (!schedule_.empty() && schedule_.top()->schedule_time_ <= now + kFudgeFactor) {
Entry* entry = const_cast<Entry*>(schedule_.top());
schedule_.pop();
if (entry->delete_) {
EraseEntry(entry);
} else if (entry->schedule_time_ != entry->time_) {
// Postponed entry.
Schedule(entry);
} else if (entry->queries_remaining_ == 0) {
// TTL expired.
std::shared_ptr<DnsResource> resource =
std::make_shared<DnsResource>(entry->name_, entry->type_);
resource->time_to_live_ = 0;
SendResource(resource, MdnsResourceSection::kExpired,
ReplyAddress::Multicast(entry->media_, entry->ip_versions_));
EraseEntry(entry);
} else {
// Need to query.
SendQuestion(std::make_shared<DnsQuestion>(entry->name_, entry->type_,
entry->request_unicast_response_),
ReplyAddress::Multicast(entry->media_, entry->ip_versions_));
// unicast response bit should be set only for the 1st query.
entry->request_unicast_response_ = false;
entry->SetNextQueryOrExpiration();
Schedule(entry);
}
}
if (!schedule_.empty()) {
PostTaskForTime([this]() { SendRenewals(); }, schedule_.top()->schedule_time_);
}
}
void ResourceRenewer::Schedule(Entry* entry) {
entry->schedule_time_ = entry->time_;
schedule_.push(entry);
}
void ResourceRenewer::EraseEntry(Entry* entry) {
// We need a unique_ptr to use as a key. We don't own |entry| here, so we
// have to be careful to release the unique_ptr later.
auto unique_entry = std::unique_ptr<Entry>(entry);
entries_.erase(unique_entry);
unique_entry.release();
}
void ResourceRenewer::Entry::SetFirstQuery(zx::time initial_query_time, zx::duration interval,
uint32_t interval_multiplier, uint32_t max_queries) {
time_ = initial_query_time;
interval_ = interval;
interval_multiplier_ = interval_multiplier;
queries_remaining_ = max_queries;
}
void ResourceRenewer::Entry::SetNextQueryOrExpiration() {
FX_DCHECK(queries_remaining_ != 0);
time_ = time_ + interval_;
interval_ = interval_ * interval_multiplier_;
--queries_remaining_;
}
} // namespace mdns