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.
 
 
 
 
 
 

221 lines
7.4 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. #include <cinttypes>
  23. #include "negcache.hh"
  24. #include "misc.hh"
  25. #include "cachecleaner.hh"
  26. #include "utility.hh"
  27. /*!
  28. * Set ne to the NegCacheEntry for the last label in qname and return true if there
  29. * was one.
  30. *
  31. * \param qname The name to look up (only the last label is used)
  32. * \param now A timeval with the current time, to check if an entry is expired
  33. * \param ne A NegCacheEntry that is filled when there is a cache entry
  34. * \return true if ne was filled out, false otherwise
  35. */
  36. bool NegCache::getRootNXTrust(const DNSName& qname, const struct timeval& now, NegCacheEntry& ne)
  37. {
  38. // Never deny the root.
  39. if (qname.isRoot())
  40. return false;
  41. // An 'ENT' QType entry, used as "whole name" in the neg-cache context.
  42. static const QType qtnull(0);
  43. DNSName lastLabel = qname.getLastLabel();
  44. negcache_t::const_iterator ni = d_negcache.find(tie(lastLabel, qtnull));
  45. while (ni != d_negcache.end() && ni->d_name == lastLabel && ni->d_auth.isRoot() && ni->d_qtype == qtnull) {
  46. // We have something
  47. if ((uint32_t)now.tv_sec < ni->d_ttd) {
  48. ne = *ni;
  49. moveCacheItemToBack<SequenceTag>(d_negcache, ni);
  50. return true;
  51. }
  52. moveCacheItemToFront<SequenceTag>(d_negcache, ni);
  53. ++ni;
  54. }
  55. return false;
  56. }
  57. /*!
  58. * Set ne to the NegCacheEntry for the qname|qtype tuple and return true
  59. *
  60. * \param qname The name to look up
  61. * \param qtype The qtype to look up
  62. * \param now A timeval with the current time, to check if an entry is expired
  63. * \param ne A NegCacheEntry that is filled when there is a cache entry
  64. * \return true if ne was filled out, false otherwise
  65. */
  66. bool NegCache::get(const DNSName& qname, const QType& qtype, const struct timeval& now, NegCacheEntry& ne, bool typeMustMatch)
  67. {
  68. const auto& idx = d_negcache.get<2>();
  69. auto range = idx.equal_range(qname);
  70. auto ni = range.first;
  71. while (ni != range.second) {
  72. // We have an entry
  73. if ((!typeMustMatch && ni->d_qtype.getCode() == 0) || ni->d_qtype == qtype) {
  74. // We match the QType or the whole name is denied
  75. auto firstIndexIterator = d_negcache.project<0>(ni);
  76. if ((uint32_t)now.tv_sec < ni->d_ttd) {
  77. // Not expired
  78. ne = *ni;
  79. moveCacheItemToBack<SequenceTag>(d_negcache, firstIndexIterator);
  80. return true;
  81. }
  82. // expired
  83. moveCacheItemToFront<SequenceTag>(d_negcache, firstIndexIterator);
  84. }
  85. ++ni;
  86. }
  87. return false;
  88. }
  89. /*!
  90. * Places ne into the negative cache, possibly overriding an existing entry.
  91. *
  92. * \param ne The NegCacheEntry to add to the cache
  93. */
  94. void NegCache::add(const NegCacheEntry& ne)
  95. {
  96. lruReplacingInsert<SequenceTag>(d_negcache, ne);
  97. }
  98. /*!
  99. * Update the validation state of an existing entry with the provided state.
  100. *
  101. * \param qname The name of the entry to replace
  102. * \param qtype The type of the entry to replace
  103. * \param newState The new validation state
  104. */
  105. void NegCache::updateValidationStatus(const DNSName& qname, const QType& qtype, const vState newState, boost::optional<uint32_t> capTTD)
  106. {
  107. auto range = d_negcache.equal_range(tie(qname, qtype));
  108. if (range.first != range.second) {
  109. range.first->d_validationState = newState;
  110. if (capTTD) {
  111. range.first->d_ttd = std::min(range.first->d_ttd, *capTTD);
  112. }
  113. }
  114. }
  115. /*!
  116. * Returns the amount of entries in the cache
  117. *
  118. * \param qname The name of the entries to be counted
  119. */
  120. uint64_t NegCache::count(const DNSName& qname) const
  121. {
  122. return d_negcache.count(tie(qname));
  123. }
  124. /*!
  125. * Returns the amount of entries in the cache for qname+qtype
  126. *
  127. * \param qname The name of the entries to be counted
  128. * \param qtype The type of the entries to be counted
  129. */
  130. uint64_t NegCache::count(const DNSName& qname, const QType qtype) const
  131. {
  132. return d_negcache.count(tie(qname, qtype));
  133. }
  134. /*!
  135. * Remove all entries for name from the cache. If subtree is true, wipe all names
  136. * underneath it.
  137. *
  138. * \param name The DNSName of the entries to wipe
  139. * \param subtree Should all entries under name be removed?
  140. */
  141. uint64_t NegCache::wipe(const DNSName& name, bool subtree)
  142. {
  143. uint64_t ret(0);
  144. if (subtree) {
  145. for (auto i = d_negcache.lower_bound(tie(name)); i != d_negcache.end();) {
  146. if (!i->d_name.isPartOf(name))
  147. break;
  148. i = d_negcache.erase(i);
  149. ret++;
  150. }
  151. return ret;
  152. }
  153. ret = count(name);
  154. auto range = d_negcache.equal_range(tie(name));
  155. d_negcache.erase(range.first, range.second);
  156. return ret;
  157. }
  158. /*!
  159. * Clear the negative cache
  160. */
  161. void NegCache::clear()
  162. {
  163. d_negcache.clear();
  164. }
  165. /*!
  166. * Perform some cleanup in the cache, removing stale entries
  167. *
  168. * \param maxEntries The maximum number of entries that may exist in the cache.
  169. */
  170. void NegCache::prune(size_t maxEntries)
  171. {
  172. pruneCollection<SequenceTag>(*this, d_negcache, maxEntries, 200);
  173. }
  174. /*!
  175. * Writes the whole negative cache to fp
  176. *
  177. * \param fp A pointer to an open FILE object
  178. */
  179. uint64_t NegCache::dumpToFile(FILE* fp)
  180. {
  181. uint64_t ret(0);
  182. struct timeval now;
  183. Utility::gettimeofday(&now, nullptr);
  184. negcache_sequence_t& sidx = d_negcache.get<SequenceTag>();
  185. for (const NegCacheEntry& ne : sidx) {
  186. ret++;
  187. fprintf(fp, "%s %" PRId64 " IN %s VIA %s ; (%s)\n", ne.d_name.toString().c_str(), static_cast<int64_t>(ne.d_ttd - now.tv_sec), ne.d_qtype.getName().c_str(), ne.d_auth.toString().c_str(), vStateToString(ne.d_validationState).c_str());
  188. for (const auto& rec : ne.authoritySOA.records) {
  189. fprintf(fp, "%s %" PRId64 " IN %s %s ; (%s)\n", rec.d_name.toString().c_str(), static_cast<int64_t>(ne.d_ttd - now.tv_sec), DNSRecordContent::NumberToType(rec.d_type).c_str(), rec.d_content->getZoneRepresentation().c_str(), vStateToString(ne.d_validationState).c_str());
  190. }
  191. for (const auto& sig : ne.authoritySOA.signatures) {
  192. fprintf(fp, "%s %" PRId64 " IN RRSIG %s ;\n", sig.d_name.toString().c_str(), static_cast<int64_t>(ne.d_ttd - now.tv_sec), sig.d_content->getZoneRepresentation().c_str());
  193. }
  194. for (const auto& rec : ne.DNSSECRecords.records) {
  195. fprintf(fp, "%s %" PRId64 " IN %s %s ; (%s)\n", rec.d_name.toString().c_str(), static_cast<int64_t>(ne.d_ttd - now.tv_sec), DNSRecordContent::NumberToType(rec.d_type).c_str(), rec.d_content->getZoneRepresentation().c_str(), vStateToString(ne.d_validationState).c_str());
  196. }
  197. for (const auto& sig : ne.DNSSECRecords.signatures) {
  198. fprintf(fp, "%s %" PRId64 " IN RRSIG %s ;\n", sig.d_name.toString().c_str(), static_cast<int64_t>(ne.d_ttd - now.tv_sec), sig.d_content->getZoneRepresentation().c_str());
  199. }
  200. }
  201. return ret;
  202. }