File: negcache.hh

package info (click to toggle)
pdns-recursor 5.3.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 11,108 kB
  • sloc: cpp: 109,513; javascript: 20,651; python: 5,657; sh: 5,069; makefile: 780; ansic: 582; xml: 37
file content (192 lines) | stat: -rw-r--r-- 6,067 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
/*
 * This file is part of PowerDNS or dnsdist.
 * Copyright -- PowerDNS.COM B.V. and its contributors
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of version 2 of the GNU General Public License as
 * published by the Free Software Foundation.
 *
 * In addition, for the avoidance of any doubt, permission is granted to
 * link this program with OpenSSL and to (re)distribute the binaries
 * produced as the result of such linking.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
#pragma once

#include <vector>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/sequenced_index.hpp>
#include <boost/multi_index/key_extractors.hpp>
#include <boost/optional.hpp>
#include "dnsparser.hh"
#include "dnsname.hh"
#include "dns.hh"
#include "lock.hh"
#include "stat_t.hh"
#include "validate.hh"

using namespace ::boost::multi_index;

/* FIXME should become part of the normal cache (I think) and should become more like
 * struct {
 *   vector<DNSRecord> records;
 *   vector<DNSRecord> signatures;
 * } recsig_t;
 *
 * typedef vector<recsig_t> recordsAndSignatures;
 */
typedef struct
{
  vector<DNSRecord> records;
  vector<DNSRecord> signatures;
} recordsAndSignatures;

class NegCache : public boost::noncopyable
{
public:
  NegCache(size_t mapsCount = 128);

  // For a description on how ServeStale works, see recursor_cache.cc, the general structure is the same.
  // The number of times a stale cache entry is extended
  static uint16_t s_maxServedStaleExtensions;
  // The time a stale cache entry is extended
  static constexpr uint32_t s_serveStaleExtensionPeriod = 30;

  struct NegCacheEntry
  {
    recordsAndSignatures authoritySOA; // The upstream SOA record and RRSIGs
    recordsAndSignatures DNSSECRecords; // The upstream NSEC(3) and RRSIGs
    DNSName d_name; // The denied name
    DNSName d_auth; // The denying name (aka auth)
    mutable time_t d_ttd; // Timestamp when this entry should die
    uint32_t d_orig_ttl;
    mutable uint16_t d_servedStale{0};
    mutable vState d_validationState{vState::Indeterminate};
    QType d_qtype; // The denied type

    bool isStale(time_t now) const
    {
      // We like to keep things in cache when we (potentially) should serve stale
      if (s_maxServedStaleExtensions > 0) {
        return d_ttd + static_cast<time_t>(s_maxServedStaleExtensions) * std::min(s_serveStaleExtensionPeriod, d_orig_ttl) < now;
      }
      else {
        return d_ttd < now;
      }
    };

    bool isEntryUsable(time_t now, bool serveStale) const
    {
      // When serving stale, we consider expired records
      return d_ttd > now || serveStale || d_servedStale != 0;
    }
  };

  void add(const NegCacheEntry& ne);
  void updateValidationStatus(const DNSName& qname, QType qtype, vState newState, boost::optional<time_t> capTTD);
  bool get(const DNSName& qname, QType qtype, const struct timeval& now, NegCacheEntry& ne, bool typeMustMatch = false, bool serverStale = false, bool refresh = false);
  bool getRootNXTrust(const DNSName& qname, const struct timeval& now, NegCacheEntry& negEntry, bool serveStale, bool refresh);
  size_t count(const DNSName& qname);
  size_t count(const DNSName& qname, QType qtype);
  void prune(time_t now, size_t maxEntries);
  void clear();
  size_t doDump(int fd, size_t maxCacheEntries, time_t now = time(nullptr));
  size_t wipe(const DNSName& name, bool subtree = false);
  size_t wipeTyped(const DNSName& name, QType qtype);
  size_t size() const;

private:
  struct CompositeKey
  {
  };
  struct SequenceTag
  {
  };
  typedef boost::multi_index_container<
    NegCacheEntry,
    indexed_by<
      ordered_unique<tag<CompositeKey>,
                     composite_key<
                       NegCacheEntry,
                       member<NegCacheEntry, DNSName, &NegCacheEntry::d_name>,
                       member<NegCacheEntry, QType, &NegCacheEntry::d_qtype>>,
                     composite_key_compare<
                       CanonDNSNameCompare, std::less<QType>>>,
      sequenced<tag<SequenceTag>>,
      hashed_non_unique<tag<NegCacheEntry>,
                        member<NegCacheEntry, DNSName, &NegCacheEntry::d_name>>>>
    negcache_t;

  void updateStaleEntry(time_t now, negcache_t::iterator& entry, QType qtype);

  struct MapCombo
  {
    MapCombo() {}
    MapCombo(const MapCombo&) = delete;
    MapCombo& operator=(const MapCombo&) = delete;
    struct LockedContent
    {
      negcache_t d_map;
      uint64_t d_contended_count{0};
      uint64_t d_acquired_count{0};
      void invalidate() {}
      void preRemoval(const NegCacheEntry& /* entry */) {}
    };

    LockGuardedTryHolder<MapCombo::LockedContent> lock()
    {
      auto locked = d_content.try_lock();
      if (!locked.owns_lock()) {
        locked.lock();
        ++locked->d_contended_count;
      }
      ++locked->d_acquired_count;
      return locked;
    }

    [[nodiscard]] auto getEntriesCount() const
    {
      return d_entriesCount.load();
    }

    void incEntriesCount()
    {
      ++d_entriesCount;
    }

    void decEntriesCount()
    {
      --d_entriesCount;
    }

    void clearEntriesCount()
    {
      d_entriesCount = 0;
    }

  private:
    LockGuarded<LockedContent> d_content;
    pdns::stat_t d_entriesCount{0};
  };

  vector<MapCombo> d_maps;

  MapCombo& getMap(const DNSName& qname)
  {
    return d_maps.at(qname.hash() % d_maps.size());
  }
  const MapCombo& getMap(const DNSName& qname) const
  {
    return d_maps.at(qname.hash() % d_maps.size());
  }
};