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.
 
 
 
 
 
 

243 lines
8.0 KiB

  1. #ifdef HAVE_CONFIG_H
  2. #include "config.h"
  3. #endif
  4. #include <iostream>
  5. #include <cinttypes>
  6. #include "recpacketcache.hh"
  7. #include "cachecleaner.hh"
  8. #include "dns.hh"
  9. #include "namespaces.hh"
  10. RecursorPacketCache::RecursorPacketCache()
  11. {
  12. d_hits = d_misses = 0;
  13. }
  14. int RecursorPacketCache::doWipePacketCache(const DNSName& name, uint16_t qtype, bool subtree)
  15. {
  16. int count=0;
  17. auto& idx = d_packetCache.get<NameTag>();
  18. for(auto iter = idx.lower_bound(name); iter != idx.end(); ) {
  19. if(subtree) {
  20. if(!iter->d_name.isPartOf(name)) { // this is case insensitive
  21. break;
  22. }
  23. }
  24. else {
  25. if(iter->d_name != name)
  26. break;
  27. }
  28. if(qtype==0xffff || iter->d_type == qtype) {
  29. iter=idx.erase(iter);
  30. count++;
  31. }
  32. else
  33. ++iter;
  34. }
  35. return count;
  36. }
  37. bool RecursorPacketCache::qrMatch(const packetCache_t::index<HashTag>::type::iterator& iter, const std::string& queryPacket, const DNSName& qname, uint16_t qtype, uint16_t qclass, uint16_t ecsBegin, uint16_t ecsEnd)
  38. {
  39. // this ignores checking on the EDNS subnet flags!
  40. if (qname != iter->d_name || iter->d_type != qtype || iter->d_class != qclass) {
  41. return false;
  42. }
  43. if (iter->d_ecsBegin != ecsBegin || iter->d_ecsEnd != ecsEnd) {
  44. return false;
  45. }
  46. return queryMatches(iter->d_query, queryPacket, qname, ecsBegin, ecsEnd);
  47. }
  48. bool RecursorPacketCache::checkResponseMatches(std::pair<packetCache_t::index<HashTag>::type::iterator, packetCache_t::index<HashTag>::type::iterator> range, const std::string& queryPacket, const DNSName& qname, uint16_t qtype, uint16_t qclass, time_t now, std::string* responsePacket, uint32_t* age, vState* valState, RecProtoBufMessage* protobufMessage, uint16_t ecsBegin, uint16_t ecsEnd)
  49. {
  50. for(auto iter = range.first ; iter != range.second ; ++iter) {
  51. // the possibility is VERY real that we get hits that are not right - birthday paradox
  52. if (!qrMatch(iter, queryPacket, qname, qtype, qclass, ecsBegin, ecsEnd)) {
  53. continue;
  54. }
  55. if (now < iter->d_ttd) { // it is right, it is fresh!
  56. *age = static_cast<uint32_t>(now - iter->d_creation);
  57. *responsePacket = iter->d_packet;
  58. responsePacket->replace(0, 2, queryPacket.c_str(), 2);
  59. *valState = iter->d_vstate;
  60. const size_t wirelength = qname.wirelength();
  61. if (responsePacket->size() > (sizeof(dnsheader) + wirelength)) {
  62. responsePacket->replace(sizeof(dnsheader), wirelength, queryPacket, sizeof(dnsheader), wirelength);
  63. }
  64. d_hits++;
  65. moveCacheItemToBack<SequencedTag>(d_packetCache, iter);
  66. #ifdef HAVE_PROTOBUF
  67. if (protobufMessage) {
  68. if (iter->d_protobufMessage) {
  69. protobufMessage->copyFrom(*(iter->d_protobufMessage));
  70. }
  71. else {
  72. *protobufMessage = RecProtoBufMessage(DNSProtoBufMessage::DNSProtoBufMessageType::Response);
  73. }
  74. }
  75. #endif
  76. return true;
  77. }
  78. else {
  79. moveCacheItemToFront<SequencedTag>(d_packetCache, iter);
  80. d_misses++;
  81. break;
  82. }
  83. }
  84. return false;
  85. }
  86. bool RecursorPacketCache::getResponsePacket(unsigned int tag, const std::string& queryPacket, time_t now,
  87. std::string* responsePacket, uint32_t* age, uint32_t* qhash)
  88. {
  89. DNSName qname;
  90. uint16_t qtype, qclass;
  91. uint16_t ecsBegin;
  92. uint16_t ecsEnd;
  93. vState valState;
  94. return getResponsePacket(tag, queryPacket, qname, &qtype, &qclass, now, responsePacket, age, &valState, qhash, &ecsBegin, &ecsEnd, nullptr);
  95. }
  96. bool RecursorPacketCache::getResponsePacket(unsigned int tag, const std::string& queryPacket, const DNSName& qname, uint16_t qtype, uint16_t qclass, time_t now,
  97. std::string* responsePacket, uint32_t* age, uint32_t* qhash)
  98. {
  99. vState valState;
  100. uint16_t ecsBegin;
  101. uint16_t ecsEnd;
  102. return getResponsePacket(tag, queryPacket, qname, qtype, qclass, now, responsePacket, age, &valState, qhash, &ecsBegin, &ecsEnd, nullptr);
  103. }
  104. bool RecursorPacketCache::getResponsePacket(unsigned int tag, const std::string& queryPacket, const DNSName& qname, uint16_t qtype, uint16_t qclass, time_t now,
  105. std::string* responsePacket, uint32_t* age, vState* valState, uint32_t* qhash, uint16_t* ecsBegin, uint16_t* ecsEnd, RecProtoBufMessage* protobufMessage)
  106. {
  107. *qhash = canHashPacket(queryPacket, ecsBegin, ecsEnd);
  108. const auto& idx = d_packetCache.get<HashTag>();
  109. auto range = idx.equal_range(tie(tag,*qhash));
  110. if(range.first == range.second) {
  111. d_misses++;
  112. return false;
  113. }
  114. return checkResponseMatches(range, queryPacket, qname, qtype, qclass, now, responsePacket, age, valState, protobufMessage, *ecsBegin, *ecsEnd);
  115. }
  116. bool RecursorPacketCache::getResponsePacket(unsigned int tag, const std::string& queryPacket, DNSName& qname, uint16_t* qtype, uint16_t* qclass, time_t now,
  117. std::string* responsePacket, uint32_t* age, vState* valState, uint32_t* qhash, uint16_t* ecsBegin, uint16_t* ecsEnd, RecProtoBufMessage* protobufMessage)
  118. {
  119. *qhash = canHashPacket(queryPacket, ecsBegin, ecsEnd);
  120. const auto& idx = d_packetCache.get<HashTag>();
  121. auto range = idx.equal_range(tie(tag,*qhash));
  122. if(range.first == range.second) {
  123. d_misses++;
  124. return false;
  125. }
  126. qname = DNSName(queryPacket.c_str(), queryPacket.length(), sizeof(dnsheader), false, qtype, qclass, 0);
  127. return checkResponseMatches(range, queryPacket, qname, *qtype, *qclass, now, responsePacket, age, valState, protobufMessage, *ecsBegin, *ecsEnd);
  128. }
  129. void RecursorPacketCache::insertResponsePacket(unsigned int tag, uint32_t qhash, std::string&& query, const DNSName& qname, uint16_t qtype, uint16_t qclass, std::string&& responsePacket, time_t now, uint32_t ttl, const vState& valState, uint16_t ecsBegin, uint16_t ecsEnd, boost::optional<RecProtoBufMessage>&& protobufMessage)
  130. {
  131. auto& idx = d_packetCache.get<HashTag>();
  132. auto range = idx.equal_range(tie(tag,qhash));
  133. auto iter = range.first;
  134. for( ; iter != range.second ; ++iter) {
  135. if (iter->d_type != qtype || iter->d_class != qclass || iter->d_name != qname) {
  136. continue;
  137. }
  138. moveCacheItemToBack<SequencedTag>(d_packetCache, iter);
  139. iter->d_packet = std::move(responsePacket);
  140. iter->d_query = std::move(query);
  141. iter->d_ecsBegin = ecsBegin;
  142. iter->d_ecsEnd = ecsEnd;
  143. iter->d_ttd = now + ttl;
  144. iter->d_creation = now;
  145. iter->d_vstate = valState;
  146. #ifdef HAVE_PROTOBUF
  147. if (protobufMessage) {
  148. iter->d_protobufMessage = std::move(*protobufMessage);
  149. }
  150. #endif
  151. break;
  152. }
  153. if(iter == range.second) { // nothing to refresh
  154. struct Entry e(qname, std::move(responsePacket), std::move(query));
  155. e.d_qhash = qhash;
  156. e.d_ecsBegin = ecsBegin;
  157. e.d_ecsEnd = ecsEnd;
  158. e.d_type = qtype;
  159. e.d_class = qclass;
  160. e.d_ttd = now+ttl;
  161. e.d_creation = now;
  162. e.d_tag = tag;
  163. e.d_vstate = valState;
  164. #ifdef HAVE_PROTOBUF
  165. if (protobufMessage) {
  166. e.d_protobufMessage = std::move(*protobufMessage);
  167. }
  168. #endif
  169. d_packetCache.insert(e);
  170. }
  171. }
  172. uint64_t RecursorPacketCache::size()
  173. {
  174. return d_packetCache.size();
  175. }
  176. uint64_t RecursorPacketCache::bytes()
  177. {
  178. uint64_t sum=0;
  179. for(const auto& e : d_packetCache) {
  180. sum += sizeof(e) + e.d_packet.length() + 4;
  181. }
  182. return sum;
  183. }
  184. void RecursorPacketCache::doPruneTo(size_t maxCached)
  185. {
  186. pruneCollection<SequencedTag>(*this, d_packetCache, maxCached);
  187. }
  188. uint64_t RecursorPacketCache::doDump(int fd)
  189. {
  190. auto fp = std::unique_ptr<FILE, int(*)(FILE*)>(fdopen(dup(fd), "w"), fclose);
  191. if(!fp) { // dup probably failed
  192. return 0;
  193. }
  194. fprintf(fp.get(), "; main packet cache dump from thread follows\n;\n");
  195. const auto& sidx=d_packetCache.get<1>();
  196. uint64_t count=0;
  197. time_t now=time(0);
  198. for(auto i=sidx.cbegin(); i != sidx.cend(); ++i) {
  199. count++;
  200. try {
  201. fprintf(fp.get(), "%s %" PRId64 " %s ; tag %d\n", i->d_name.toString().c_str(), static_cast<int64_t>(i->d_ttd - now), DNSRecordContent::NumberToType(i->d_type).c_str(), i->d_tag);
  202. }
  203. catch(...) {
  204. fprintf(fp.get(), "; error printing '%s'\n", i->d_name.empty() ? "EMPTY" : i->d_name.toString().c_str());
  205. }
  206. }
  207. return count;
  208. }