]> 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 fdfc48a710a4f9b961099823d793008aa35e72af..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 "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, bool recursive, 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
+  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);
+    }
 
-  void insert(const DNSName &qname, const QType& qtype, CacheEntryType cet, const string& value, unsigned int ttl, int zoneID=-1, bool meritsRecursion=false,
-    unsigned int maxReplyLen=512, bool dnssecOk=false, bool EDNS=false);
+    return ret;
+  }
 
-  void insert(const DNSName &qname, const QType& qtype, CacheEntryType cet, const vector<DNSResourceRecord>& 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);
+    }
 
-  int get(DNSPacket *p, DNSPacket *q, bool recursive); //!< 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,
-    bool meritsRecursion=false, unsigned int maxReplyLen=512, bool dnssecOk=false, bool hasEDNS=false, unsigned int *age=0);
-  bool getEntry(const DNSName &qname, const QType& qtype, CacheEntryType cet, vector<DNSResourceRecord>& entry, int zoneID=-1);
-  
+    return ret;
+  }
 
-  int size(); //!< number of entries in the cache
-  void cleanupIfNeeded()
+  static bool queryHeaderMatches(const std::string& cachedQuery, const std::string& query)
   {
-    if(!(++d_ops % 300000)) {
-      if(d_lastclean + 30 < time(0)) {
-        d_lastclean=time(0); 
-        cleanup();
-      }
+    if (cachedQuery.size() != query.size()) {
+      return false;
     }
+
+    return (cachedQuery.compare(/* skip the ID */ 2, sizeof(dnsheader) - 2, query, 2, sizeof(dnsheader) - 2) == 0);
   }
-  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
 
-  map<char,int> getCounts();
-private:
-  bool getEntryLocked(const DNSName &content, const QType& qtype, CacheEntryType cet, string& entry, int zoneID=-1,
-    bool meritsRecursion=false, unsigned int maxReplyLen=512, bool dnssecOk=false, bool hasEDNS=false, unsigned int *age=0);
-  bool getEntryLocked(const DNSName &content, const QType& qtype, CacheEntryType cet, vector<DNSResourceRecord>& 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; meritsRecursion=false; dnssecOk=false; hasEDNS=false; created=0; ttd=0; maxReplyLen=512;}
-
-    DNSName qname;
-    string value;
-    vector<DNSResourceRecord> drs;
-    time_t created;
-    time_t ttd;
-
-    uint16_t qtype;
-    uint16_t ctype;
-    int zoneID;
-    unsigned int maxReplyLen;
-
-    bool meritsRecursion;
-    bool dnssecOk;
-    bool hasEDNS;
-  };
-
-  void getTTLS();
-
-  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,bool, &CacheEntry::meritsRecursion>,
-                        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<bool>, 
-                          std::less<unsigned int>, std::less<bool>, std::less<bool> >
-                            >,
-                           sequenced<>
-                           >
-  > 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;
+    }
 
-  AtomicCounter d_ops;
-  time_t d_lastclean{0}; // doesn't need to be atomic
-  AtomicCounter *d_statnumhit;
-  AtomicCounter *d_statnummiss;
-  AtomicCounter *d_statnumentries;
+    size_t pos = sizeof(dnsheader) + qname.wirelength();
 
-  int d_ttl;
-  int d_recursivettl;
-  bool d_doRecursion;
-};
+    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;
+  }
+
+};
 
 #endif /* PACKETCACHE_HH */