1 /*
2  * This file is part of PowerDNS or dnsdist.
3  * Copyright -- PowerDNS.COM B.V. and its contributors
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of version 2 of the GNU General Public License as
7  * published by the Free Software Foundation.
8  *
9  * In addition, for the avoidance of any doubt, permission is granted to
10  * link this program with OpenSSL and to (re)distribute the binaries
11  * produced as the result of such linking.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21  */
22 #pragma once
23 #include <string>
24 #include <map>
25 #include "dns.hh"
26 #include <boost/version.hpp>
27 #include "namespaces.hh"
28 using namespace ::boost::multi_index;
29 
30 #include <boost/multi_index/hashed_index.hpp>
31 
32 #include "dns.hh"
33 #include "dnspacket.hh"
34 #include "lock.hh"
35 
36 class AuthQueryCache : public boost::noncopyable
37 {
38 public:
39   AuthQueryCache(size_t mapsCount=1024);
40   ~AuthQueryCache();
41 
42   void insert(const DNSName &qname, const QType& qtype, vector<DNSZoneRecord>&& content, uint32_t ttl, int zoneID);
43 
44   bool getEntry(const DNSName &qname, const QType& qtype, vector<DNSZoneRecord>& entry, int zoneID);
45 
size()46   size_t size() { return *d_statnumentries; } //!< number of entries in the cache
47   void cleanup(); //!< force the cache to preen itself from expired queries
48   uint64_t purge();
49   uint64_t purge(const std::string& match); // could be $ terminated. Is not a dnsname!
50   uint64_t purgeExact(const DNSName& qname); // no wildcard matching here
51 
52   map<char,uint64_t> getCounts();
53 
setMaxEntries(uint64_t maxEntries)54   void setMaxEntries(uint64_t maxEntries)
55   {
56     d_maxEntries = maxEntries;
57     for (auto& shard : d_maps) {
58       shard.reserve(maxEntries / d_maps.size());
59     }
60   }
61 private:
62 
63   struct CacheEntry
64   {
65     DNSName qname;
66     mutable vector<DNSZoneRecord> drs;
67     mutable time_t created{0};
68     mutable time_t ttd{0};
69     uint16_t qtype{0};
70     int zoneID{-1};
71   };
72 
73   struct HashTag{};
74   struct NameTag{};
75   struct SequencedTag{};
76   typedef multi_index_container<
77     CacheEntry,
78     indexed_by <
79       hashed_unique<tag<HashTag>, composite_key<CacheEntry,
80                                                          member<CacheEntry,DNSName,&CacheEntry::qname>,
81                                                          member<CacheEntry,uint16_t,&CacheEntry::qtype>,
82                                                          member<CacheEntry,int, &CacheEntry::zoneID> > > ,
83       ordered_non_unique<tag<NameTag>, member<CacheEntry,DNSName,&CacheEntry::qname>, CanonDNSNameCompare >,
84       /* Note that this sequence holds 'least recently inserted or replaced', not least recently used.
85          Making it a LRU would require taking a write-lock when fetching from the cache, making the RW-lock inefficient compared to a mutex */
86       sequenced<tag<SequencedTag>>
87                            >
88   > cmap_t;
89 
90 
91   struct MapCombo
92   {
MapComboAuthQueryCache::MapCombo93     MapCombo() {
94     }
~MapComboAuthQueryCache::MapCombo95     ~MapCombo() {
96     }
97     MapCombo(const MapCombo &) = delete;
98     MapCombo & operator=(const MapCombo &) = delete;
99 
100     void reserve(size_t numberOfEntries);
101 
102     ReadWriteLock d_mut;
103     cmap_t d_map;
104   };
105 
106   vector<MapCombo> d_maps;
getMap(const DNSName & qname)107   MapCombo& getMap(const DNSName& qname)
108   {
109     return d_maps[qname.hash() % d_maps.size()];
110   }
111 
112   bool getEntryLocked(cmap_t& map, const DNSName &content, uint16_t qtype, vector<DNSZoneRecord>& entry, int zoneID, time_t now);
113   void cleanupIfNeeded();
114 
115   AtomicCounter d_ops{0};
116   AtomicCounter *d_statnumhit;
117   AtomicCounter *d_statnummiss;
118   AtomicCounter *d_statnumentries;
119 
120   uint64_t d_maxEntries{0};
121   time_t d_lastclean; // doesn't need to be atomic
122   unsigned long d_nextclean{4096};
123   unsigned int d_cleaninterval{4096};
124   bool d_cleanskipped{false};
125 
126   static const unsigned int s_mincleaninterval=1000, s_maxcleaninterval=300000;
127 };
128