blob: fe851070587d28d3b00840235268d07685e61894 [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 <lib/syslog/cpp/macros.h>
#include <lib/zx/clock.h>
#include <lib/zx/time.h>
#include <memory>
#include <queue>
#include <string>
#include <unordered_set>
#include <vector>
#include "src/connectivity/network/mdns/service/agents/mdns_agent.h"
#include "src/connectivity/network/mdns/service/common/types.h"
#include "src/lib/inet/ip_port.h"
namespace mdns {
// |ResourceRenewer| requests resources by sending queries repeatedly. This capability can be used
// to renew a received resource (using the |Renew| method) or for general querying (using the
// |Query| method). When renewing a received resource, queries are sent at 80%, 85%, 90% and 95%
// of the resource's TTL.
// If a resource is received, the renewer forgets about the resource until asked again to renew it.
// If the resource is not received after the complete query sequence, |ResourceRenewer| sends a
// resource record to all the agents with a TTL of zero, signalling that the resource should be
// deleted and forgets about the resource. If a resource is explicitly deleted (a resource
// record arrives with TTL 0), |ResourceRenewer| will stop querying for it.
// Agents that need a resource record renewed call |Renew| on the host, which
// then calls |Renew| on the |ResourceRenewer|. Agents must continue to renew
// incoming resources as long as they want renewals to occur. When an agent
// loses interest in a record, it should simply stop renewing the incoming
// resource records. This approach will cause some unneeded renewals, but avoids
// difficult cleanup issues associated with a persistent renewal scheme.
class ResourceRenewer : public MdnsAgent {
explicit ResourceRenewer(MdnsAgent::Owner* owner);
~ResourceRenewer() override;
// Attempts to renew |resource| before its TTL expires.
void Renew(const DnsResource& resource, Media media, IpVersions ip_versions);
// Queries for the indicated resource with the specified schedule.
void 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);
// MdnsAgent overrides.
void ReceiveResource(const DnsResource& resource, MdnsResourceSection section,
ReplyAddress sender_address) override;
void Quit() override;
static constexpr uint32_t kFirstQueryPerThousand = 800;
static constexpr uint32_t kQueryIntervalPerThousand = 50;
static constexpr uint32_t kQueriesToAttempt = 4;
static constexpr zx::duration kFudgeFactor = zx::msec(1);
// All Entry objects are represented in both |entries_| and |schedule_|. We're
// using raw pointers, so the destructor must delete all Entry objects
// explicitly.
struct Entry {
Entry(std::string name, DnsType type, Media media, IpVersions ip_versions)
: name_(std::move(name)), type_(type), media_(media), ip_versions_(ip_versions) {}
std::string name_;
DnsType type_;
Media media_;
IpVersions ip_versions_;
zx::time time_;
zx::duration interval_;
uint32_t interval_multiplier_;
uint32_t queries_remaining_;
// Time value used for |schedule|. In some cases, we want to postpone a
// query or expiration that was previously scheduled. In this case, |time_|
// will be increased, but |schedule_time_| will remain unchanged. When the
// entry comes up in the schedule, the entry should be rescheduled if
// |time_| is different from |schedule_time_|.
zx::time schedule_time_;
bool delete_ = false;
// Sets |time_|, |interval_|, |interval_multiplier_| and |queries_remaining_| to their initial
// values.
void SetFirstQuery(zx::time initial_query_time, zx::duration interval,
uint32_t interval_multiplier, uint32_t max_queries);
// Updates |time_| and |queries_remaining_| for the purposes of scheduling
// the next query or expiration.
void SetNextQueryOrExpiration();
struct Hash {
size_t operator()(const std::unique_ptr<Entry>& m) const {
return std::hash<std::string>{}(m->name_) ^ std::hash<DnsType>{}(m->type_);
struct Equals {
size_t operator()(const std::unique_ptr<Entry>& a, const std::unique_ptr<Entry>& b) const {
return a->name_ == b->name_ && a->type_ == b->type_;
struct LaterScheduleTime {
size_t operator()(const Entry* a, const Entry* b) {
FX_DCHECK(a != nullptr);
FX_DCHECK(b != nullptr);
return a->schedule_time_ > b->schedule_time_;
// Sends current renewals and schedules another call to |SendRenewals|, as
// appropriate.
void SendRenewals();
void Schedule(Entry* entry);
void EraseEntry(Entry* entry);
std::unordered_set<std::unique_ptr<Entry>, Hash, Equals> entries_;
std::priority_queue<Entry*, std::vector<Entry*>, LaterScheduleTime> schedule_;
// Disallow copy, assign and move.
ResourceRenewer(const ResourceRenewer&) = delete;
ResourceRenewer(ResourceRenewer&&) = delete;
ResourceRenewer& operator=(const ResourceRenewer&) = delete;
ResourceRenewer& operator=(ResourceRenewer&&) = delete;
} // namespace mdns