]> git.ipfire.org Git - thirdparty/pdns.git/blobdiff - pdns/packetcache.hh
Merge pull request #7945 from pieterlexis/syncres-CNAME-cache-cleanup
[thirdparty/pdns.git] / pdns / packetcache.hh
index 357acedd1aa2f2ea0f5dc38f6fc1c0d9bd1e4be1..7b2f1a8b38b6551b08a6179944450704d6ff4ed8 100644 (file)
 #ifndef PACKETCACHE_HH
 #define PACKETCACHE_HH
 
-#include <string>
-#include <utility>
-#include <map>
-#include <map>
-#include "dns.hh"
-#include <boost/version.hpp>
-#include "namespaces.hh"
-using namespace ::boost::multi_index;
-
-#include "namespaces.hh"
-#include <boost/multi_index/hashed_index.hpp> 
-#include "dnspacket.hh"
-#include "lock.hh"
-#include "statbag.hh"
-
-/** This class performs 'whole packet caching'. Feed it a question packet and it will
-    try to find an answer. If you have an answer, insert it to have it cached for later use. 
-    Take care not to replace existing cache entries. While this works, it is wasteful. Only
-    insert packets that where not found by get()
-
-    Locking! 
-
-    The cache itself is protected by a read/write lock. Because deleting is a two step process, which 
-    first marks and then sweeps, a second lock is present to prevent simultaneous inserts and deletes.
-*/
+#include "ednsoptions.hh"
+#include "misc.hh"
+#include "iputils.hh"
 
 class PacketCache : public boost::noncopyable
 {
 public:
-  PacketCache();
-  ~PacketCache();
-  enum CacheEntryType { PACKETCACHE, QUERYCACHE};
-
-  void insert(DNSPacket *q, DNSPacket *r, unsigned int maxttl=UINT_MAX);  //!< We copy the contents of *p into our cache. Do not needlessly call this to insert questions already in the cache as it wastes resources
-
-  void insert(const DNSName &qname, const QType& qtype, CacheEntryType cet, const string& value, unsigned int ttl, int zoneID=-1,
-    unsigned int maxReplyLen=512, bool dnssecOk=false, bool EDNS=false);
+  static uint32_t canHashPacket(const std::string& packet, uint16_t* ecsBegin, uint16_t* ecsEnd)
+  {
+    uint32_t ret = 0;
+    ret = burtle(reinterpret_cast<const unsigned char*>(packet.c_str()) + 2, sizeof(dnsheader) - 2, ret); // rest of dnsheader, skip id
+    size_t packetSize = packet.size();
+    size_t pos = sizeof(dnsheader);
+    const char* end = packet.c_str() + packetSize;
+    const char* p = packet.c_str() + pos;
+
+    for(; p < end && *p; ++p, ++pos) { // XXX if you embed a 0 in your qname we'll stop lowercasing there
+      const unsigned char l = dns_tolower(*p); // label lengths can safely be lower cased
+      ret=burtle(&l, 1, ret);
+    }                           // XXX the embedded 0 in the qname will break the subnet stripping
+
+    const struct dnsheader* dh = reinterpret_cast<const struct dnsheader*>(packet.c_str());
+    const char* skipBegin = p;
+    const char* skipEnd = p;
+    if (ecsBegin != nullptr && ecsEnd != nullptr) {
+      *ecsBegin = 0;
+      *ecsEnd = 0;
+    }
+    /* we need at least 1 (final empty label) + 2 (QTYPE) + 2 (QCLASS)
+       + OPT root label (1), type (2), class (2) and ttl (4)
+       + the OPT RR rdlen (2)
+       = 16
+    */
+    if(ntohs(dh->arcount)==1 && (pos+16) < packetSize) {
+      char* optionBegin = nullptr;
+      size_t optionLen = 0;
+      /* skip the final empty label (1), the qtype (2), qclass (2) */
+      /* root label (1), type (2), class (2) and ttl (4) */
+      int res = getEDNSOption(const_cast<char*>(reinterpret_cast<const char*>(p)) + 14, end - (p + 14), EDNSOptionCode::ECS, &optionBegin, &optionLen);
+      if (res == 0) {
+        skipBegin = optionBegin;
+        skipEnd = optionBegin + optionLen;
+        if (ecsBegin != nullptr && ecsEnd != nullptr) {
+          *ecsBegin = optionBegin - packet.c_str();
+          *ecsEnd = *ecsBegin + optionLen;
+        }
+      }
+    }
+    if (skipBegin > p) {
+      ret = burtle(reinterpret_cast<const unsigned char*>(p), skipBegin-p, ret);
+    }
+    if (skipEnd < end) {
+      ret = burtle(reinterpret_cast<const unsigned char*>(skipEnd), end-skipEnd, ret);
+    }
+
+    return ret;
+  }
 
-  void insert(const DNSName &qname, const QType& qtype, CacheEntryType cet, const vector<DNSZoneRecord>& content, unsigned int ttl, int zoneID=-1);
+  static uint32_t canHashPacket(const std::string& packet)
+  {
+    uint32_t ret = 0;
+    ret = burtle(reinterpret_cast<const unsigned char*>(packet.c_str()) + 2, sizeof(dnsheader) - 2, ret); // rest of dnsheader, skip id
+    size_t packetSize = packet.size();
+    size_t pos = sizeof(dnsheader);
+    const char* end = packet.c_str() + packetSize;
+    const char* p = packet.c_str() + pos;
+
+    for(; p < end && *p; ++p) { // XXX if you embed a 0 in your qname we'll stop lowercasing there
+      const unsigned char l = dns_tolower(*p); // label lengths can safely be lower cased
+      ret=burtle(&l, 1, ret);
+    }                           // XXX the embedded 0 in the qname will break the subnet stripping
+
+    if (p < end) {
+      ret = burtle(reinterpret_cast<const unsigned char*>(p), end-p, ret);
+    }
+
+    return ret;
+  }
 
-  int get(DNSPacket *p, DNSPacket *q); //!< We return a dynamically allocated copy out of our cache. You need to delete it. You also need to spoof in the right ID with the DNSPacket.spoofID() method.
-  bool getEntry(const DNSName &qname, const QType& qtype, CacheEntryType cet, string& entry, int zoneID=-1,
-    unsigned int maxReplyLen=512, bool dnssecOk=false, bool hasEDNS=false);
-  bool getEntry(const DNSName &qname, const QType& qtype, CacheEntryType cet, vector<DNSZoneRecord>& entry, int zoneID=-1);
-  
+  static bool queryHeaderMatches(const std::string& cachedQuery, const std::string& query)
+  {
+    if (cachedQuery.size() != query.size()) {
+      return false;
+    }
 
-  int size() { return *d_statnumentries; } //!< number of entries in the cache
-  void cleanupIfNeeded();
-  void cleanup(); //!< force the cache to preen itself from expired packets
-  int purge();
-  int purge(const std::string& match); // could be $ terminated. Is not a dnsname!
-  int purgeExact(const DNSName& qname); // no wildcard matching here
+    return (cachedQuery.compare(/* skip the ID */ 2, sizeof(dnsheader) - 2, query, 2, sizeof(dnsheader) - 2) == 0);
+  }
 
-  map<char,int> getCounts();
-private:
-  bool getEntryLocked(const DNSName &content, const QType& qtype, CacheEntryType cet, string& entry, int zoneID=-1,
-    unsigned int maxReplyLen=512, bool dnssecOk=false, bool hasEDNS=false);
-  bool getEntryLocked(const DNSName &content, const QType& qtype, CacheEntryType cet, vector<DNSZoneRecord>& entry, int zoneID=-1);
+  static bool queryMatches(const std::string& cachedQuery, const std::string& query, const DNSName& qname)
+  {
+    if (!queryHeaderMatches(cachedQuery, query)) {
+      return false;
+    }
 
+    size_t pos = sizeof(dnsheader) + qname.wirelength();
 
-  struct CacheEntry
-  {
-    CacheEntry() { qtype = ctype = 0; zoneID = -1; dnssecOk=false; hasEDNS=false; created=0; ttd=0; maxReplyLen=512;}
-
-    DNSName qname;
-    string value;
-    vector<DNSZoneRecord> drs;
-    time_t created;
-    time_t ttd;
-
-    uint16_t qtype;
-    uint16_t ctype;
-    int zoneID;
-    unsigned int maxReplyLen;
-
-    bool dnssecOk;
-    bool hasEDNS;
-  };
-
-  void getTTLS();
-
-  struct UnorderedNameTag{};
-  struct SequenceTag{};
-  typedef multi_index_container<
-    CacheEntry,
-    indexed_by <
-                ordered_unique<
-                      composite_key< 
-                        CacheEntry,
-                        member<CacheEntry,DNSName,&CacheEntry::qname>,
-                        member<CacheEntry,uint16_t,&CacheEntry::qtype>,
-                        member<CacheEntry,uint16_t, &CacheEntry::ctype>,
-                        member<CacheEntry,int, &CacheEntry::zoneID>,
-                        member<CacheEntry,unsigned int, &CacheEntry::maxReplyLen>,
-                        member<CacheEntry,bool, &CacheEntry::dnssecOk>,
-                        member<CacheEntry,bool, &CacheEntry::hasEDNS>
-                        >,
-                      composite_key_compare<CanonDNSNameCompare, std::less<uint16_t>, std::less<uint16_t>, std::less<int>,
-                          std::less<unsigned int>, std::less<bool>, std::less<bool> >
-                       >,
-      hashed_non_unique<tag<UnorderedNameTag>, composite_key<CacheEntry,
-                                                             member<CacheEntry,DNSName,&CacheEntry::qname>,
-                                                             member<CacheEntry,uint16_t,&CacheEntry::qtype>,
-                                                             member<CacheEntry,uint16_t, &CacheEntry::ctype>,
-                                                             member<CacheEntry,int, &CacheEntry::zoneID> > > ,
-      sequenced<tag<SequenceTag>>
-                           >
-  > cmap_t;
-
-
-  struct MapCombo
-  {
-    pthread_rwlock_t d_mut;    
-    cmap_t d_map;
-  };
+    return (cachedQuery.compare(pos, cachedQuery.size() - pos, query, pos, query.size() - pos) == 0);
+  }
 
-  vector<MapCombo> d_maps;
-  MapCombo& getMap(const DNSName& qname) 
+  static bool queryMatches(const std::string& cachedQuery, const std::string& query, const DNSName& qname, uint16_t ecsBegin, uint16_t ecsEnd)
   {
-    return d_maps[qname.hash() % d_maps.size()];
+    if (!queryHeaderMatches(cachedQuery, query)) {
+      return false;
+    }
+
+    size_t pos = sizeof(dnsheader) + qname.wirelength();
+
+    if (ecsBegin != 0 && ecsBegin >= pos && ecsEnd > ecsBegin) {
+      if (cachedQuery.compare(pos, ecsBegin - pos, query, pos, ecsBegin - pos) != 0) {
+        return false;
+      }
+
+      if (cachedQuery.compare(ecsEnd, cachedQuery.size() - ecsEnd, query, ecsEnd, query.size() - ecsEnd) != 0) {
+        return false;
+      }
+    }
+    else {
+      if (cachedQuery.compare(pos, cachedQuery.size() - pos, query, pos, query.size() - pos) != 0) {
+        return false;
+      }
+    }
+
+    return true;
   }
 
-  AtomicCounter d_ops;
-  time_t d_lastclean; // doesn't need to be atomic
-  unsigned long d_nextclean;
-  unsigned int d_cleaninterval;
-  bool d_cleanskipped;
-  AtomicCounter *d_statnumhit;
-  AtomicCounter *d_statnummiss;
-  AtomicCounter *d_statnumentries;
-
-  int d_ttl;
-
-  static const unsigned int s_mincleaninterval=1000, s_maxcleaninterval=300000;
 };
 
-
-
 #endif /* PACKETCACHE_HH */