You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

268 lines
9.1 KiB

  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 <set>
  25. #include <mutex>
  26. #include "dns.hh"
  27. #include "qtype.hh"
  28. #include "misc.hh"
  29. #include "dnsname.hh"
  30. #include <iostream>
  31. #include "dnsrecords.hh"
  32. #include <boost/utility.hpp>
  33. #include <boost/multi_index_container.hpp>
  34. #include <boost/multi_index/ordered_index.hpp>
  35. #include <boost/multi_index/hashed_index.hpp>
  36. #include <boost/tuple/tuple_comparison.hpp>
  37. #include <boost/multi_index/key_extractors.hpp>
  38. #include <boost/multi_index/sequenced_index.hpp>
  39. #include <boost/version.hpp>
  40. #include "iputils.hh"
  41. #include "validate.hh"
  42. #undef max
  43. #include "namespaces.hh"
  44. using namespace ::boost::multi_index;
  45. class MemRecursorCache : public boost::noncopyable // : public RecursorCache
  46. {
  47. public:
  48. MemRecursorCache(size_t mapsCount = 1024);
  49. ~MemRecursorCache();
  50. size_t size();
  51. size_t bytes();
  52. pair<uint64_t,uint64_t> stats();
  53. size_t ecsIndexSize();
  54. typedef boost::optional<std::string> OptTag;
  55. int32_t get(time_t, const DNSName &qname, const QType& qt, bool requireAuth, vector<DNSRecord>* res, const ComboAddress& who, const OptTag& routingTag = boost::none, vector<std::shared_ptr<RRSIGRecordContent>>* signatures=nullptr, std::vector<std::shared_ptr<DNSRecord>>* authorityRecs=nullptr, bool* variable=nullptr, vState* state=nullptr, bool* wasAuth=nullptr);
  56. void replace(time_t, const DNSName &qname, const QType& qt, const vector<DNSRecord>& content, const vector<shared_ptr<RRSIGRecordContent>>& signatures, const std::vector<std::shared_ptr<DNSRecord>>& authorityRecs, bool auth, boost::optional<Netmask> ednsmask=boost::none, const OptTag& routingTag = boost::none, vState state=vState::Indeterminate);
  57. void doPrune(size_t keep);
  58. uint64_t doDump(int fd);
  59. size_t doWipeCache(const DNSName& name, bool sub, uint16_t qtype=0xffff);
  60. bool doAgeCache(time_t now, const DNSName& name, uint16_t qtype, uint32_t newTTL);
  61. bool updateValidationStatus(time_t now, const DNSName &qname, const QType& qt, const ComboAddress& who, const OptTag& routingTag, bool requireAuth, vState newState, boost::optional<time_t> capTTD);
  62. std::atomic<uint64_t> cacheHits{0}, cacheMisses{0};
  63. private:
  64. struct CacheEntry
  65. {
  66. CacheEntry(const boost::tuple<DNSName, uint16_t, OptTag, Netmask>& key, bool auth):
  67. d_qname(key.get<0>()), d_netmask(key.get<3>().getNormalized()), d_rtag(key.get<2>()), d_state(vState::Indeterminate), d_ttd(0), d_qtype(key.get<1>()), d_auth(auth)
  68. {
  69. }
  70. typedef vector<std::shared_ptr<DNSRecordContent>> records_t;
  71. time_t getTTD() const
  72. {
  73. return d_ttd;
  74. }
  75. records_t d_records;
  76. std::vector<std::shared_ptr<RRSIGRecordContent>> d_signatures;
  77. std::vector<std::shared_ptr<DNSRecord>> d_authorityRecs;
  78. DNSName d_qname;
  79. Netmask d_netmask;
  80. OptTag d_rtag;
  81. mutable vState d_state;
  82. mutable time_t d_ttd;
  83. uint16_t d_qtype;
  84. bool d_auth;
  85. };
  86. /* The ECS Index (d_ecsIndex) keeps track of whether there is any ECS-specific
  87. entry for a given (qname,qtype) entry in the cache (d_map), and if so
  88. provides a NetmaskTree of those ECS entries.
  89. This allows figuring out quickly if we should look for an entry
  90. specific to the requestor IP, and if so which entry is the most
  91. specific one.
  92. Keeping the entries in the regular cache is currently necessary
  93. because of the way we manage expired entries (moving them to the
  94. front of the expunge queue to be deleted at a regular interval).
  95. */
  96. class ECSIndexEntry
  97. {
  98. public:
  99. ECSIndexEntry(const DNSName& qname, uint16_t qtype): d_nmt(), d_qname(qname), d_qtype(qtype)
  100. {
  101. }
  102. Netmask lookupBestMatch(const ComboAddress& addr) const
  103. {
  104. const auto best = d_nmt.lookup(addr);
  105. if (best != nullptr) {
  106. return best->first;
  107. }
  108. return Netmask();
  109. }
  110. void addMask(const Netmask& nm) const
  111. {
  112. d_nmt.insert(nm).second = true;
  113. }
  114. void removeNetmask(const Netmask& nm) const
  115. {
  116. d_nmt.erase(nm);
  117. }
  118. bool isEmpty() const
  119. {
  120. return d_nmt.empty();
  121. }
  122. mutable NetmaskTree<bool> d_nmt;
  123. DNSName d_qname;
  124. uint16_t d_qtype;
  125. };
  126. struct HashedTag {};
  127. struct SequencedTag {};
  128. struct NameAndRTagOnlyHashedTag {};
  129. struct OrderedTag {};
  130. typedef multi_index_container<
  131. CacheEntry,
  132. indexed_by <
  133. ordered_unique<tag<OrderedTag>,
  134. composite_key<
  135. CacheEntry,
  136. member<CacheEntry,DNSName,&CacheEntry::d_qname>,
  137. member<CacheEntry,uint16_t,&CacheEntry::d_qtype>,
  138. member<CacheEntry,OptTag,&CacheEntry::d_rtag>,
  139. member<CacheEntry,Netmask,&CacheEntry::d_netmask>
  140. >,
  141. composite_key_compare<CanonDNSNameCompare, std::less<uint16_t>, std::less<OptTag>, std::less<Netmask> >
  142. >,
  143. sequenced<tag<SequencedTag> >,
  144. hashed_non_unique<tag<NameAndRTagOnlyHashedTag>,
  145. composite_key<
  146. CacheEntry,
  147. member<CacheEntry,DNSName,&CacheEntry::d_qname>,
  148. member<CacheEntry,OptTag,&CacheEntry::d_rtag>
  149. >
  150. >
  151. >
  152. > cache_t;
  153. typedef MemRecursorCache::cache_t::index<MemRecursorCache::OrderedTag>::type::iterator OrderedTagIterator_t;
  154. typedef MemRecursorCache::cache_t::index<MemRecursorCache::NameAndRTagOnlyHashedTag>::type::iterator NameAndRTagOnlyHashedTagIterator_t;
  155. typedef multi_index_container<
  156. ECSIndexEntry,
  157. indexed_by <
  158. hashed_unique <tag<HashedTag>,
  159. composite_key<
  160. ECSIndexEntry,
  161. member<ECSIndexEntry,DNSName,&ECSIndexEntry::d_qname>,
  162. member<ECSIndexEntry,uint16_t,&ECSIndexEntry::d_qtype>
  163. >
  164. >,
  165. ordered_unique<tag<OrderedTag>,
  166. composite_key<
  167. ECSIndexEntry,
  168. member<ECSIndexEntry,DNSName,&ECSIndexEntry::d_qname>,
  169. member<ECSIndexEntry,uint16_t,&ECSIndexEntry::d_qtype>
  170. >,
  171. composite_key_compare<CanonDNSNameCompare, std::less<uint16_t> >
  172. >
  173. >
  174. > ecsIndex_t;
  175. typedef std::pair<NameAndRTagOnlyHashedTagIterator_t, NameAndRTagOnlyHashedTagIterator_t> Entries;
  176. struct MapCombo
  177. {
  178. MapCombo() {}
  179. MapCombo(const MapCombo &) = delete;
  180. MapCombo & operator=(const MapCombo &) = delete;
  181. cache_t d_map;
  182. ecsIndex_t d_ecsIndex;
  183. DNSName d_cachedqname;
  184. OptTag d_cachedrtag;
  185. Entries d_cachecache;
  186. std::mutex mutex;
  187. bool d_cachecachevalid{false};
  188. std::atomic<uint64_t> d_entriesCount{0};
  189. uint64_t d_contended_count{0};
  190. uint64_t d_acquired_count{0};
  191. };
  192. vector<MapCombo> d_maps;
  193. MapCombo& getMap(const DNSName &qname)
  194. {
  195. return d_maps[qname.hash() % d_maps.size()];
  196. }
  197. bool entryMatches(OrderedTagIterator_t& entry, uint16_t qt, bool requireAuth, const ComboAddress& who);
  198. Entries getEntries(MapCombo& map, const DNSName &qname, const QType& qt, const OptTag& rtag);
  199. cache_t::const_iterator getEntryUsingECSIndex(MapCombo& map, time_t now, const DNSName &qname, uint16_t qtype, bool requireAuth, const ComboAddress& who);
  200. int32_t handleHit(MapCombo& map, OrderedTagIterator_t& entry, const DNSName& qname, vector<DNSRecord>* res, vector<std::shared_ptr<RRSIGRecordContent>>* signatures, std::vector<std::shared_ptr<DNSRecord>>* authorityRecs, bool* variable, boost::optional<vState>& state, bool* wasAuth);
  201. public:
  202. struct lock {
  203. lock(MapCombo& map) : m(map.mutex)
  204. {
  205. if (!m.try_lock()) {
  206. m.lock();
  207. map.d_contended_count++;
  208. }
  209. map.d_acquired_count++;
  210. }
  211. ~lock() {
  212. m.unlock();
  213. }
  214. private:
  215. std::mutex &m;
  216. };
  217. void preRemoval(const CacheEntry& entry)
  218. {
  219. if (entry.d_netmask.empty()) {
  220. return;
  221. }
  222. auto key = tie(entry.d_qname, entry.d_qtype);
  223. auto& map = getMap(entry.d_qname);
  224. auto ecsIndexEntry = map.d_ecsIndex.find(key);
  225. if (ecsIndexEntry != map.d_ecsIndex.end()) {
  226. ecsIndexEntry->removeNetmask(entry.d_netmask);
  227. if (ecsIndexEntry->isEmpty()) {
  228. map.d_ecsIndex.erase(ecsIndexEntry);
  229. }
  230. }
  231. }
  232. };
  233. namespace boost {
  234. size_t hash_value(const MemRecursorCache::OptTag& rtag);
  235. }