diff -ru pdns-4.1.4.orig/pdns/auth-packetcache.cc pdns-4.1.4/pdns/auth-packetcache.cc
--- pdns-4.1.4.orig/pdns/auth-packetcache.cc	2018-08-29 16:07:05.000000000 +0200
+++ pdns-4.1.4/pdns/auth-packetcache.cc	2018-10-09 16:21:00.048215463 +0200
@@ -72,7 +72,7 @@
     return false;
   }
 
-  uint32_t hash = canHashPacket(p->getString(), false);
+  uint32_t hash = canHashPacket(p->getString());
   p->setHash(hash);
 
   string value;
@@ -86,7 +86,7 @@
       return false;
     }
 
-    haveSomething = getEntryLocked(mc.d_map, hash, p->qdomain, p->qtype.getCode(), p->d_tcp, now, value);
+    haveSomething = getEntryLocked(mc.d_map, p->getString(), hash, p->qdomain, p->qtype.getCode(), p->d_tcp, now, value);
   }
 
   if (!haveSomething) {
@@ -106,6 +106,11 @@
   return true;
 }
 
+bool AuthPacketCache::entryMatches(cmap_t::index<HashTag>::type::iterator& iter, const std::string& query, const DNSName& qname, uint16_t qtype, bool tcp)
+{
+  return iter->tcp == tcp && iter->qtype == qtype && iter->qname == qname && queryMatches(iter->query, query, qname);
+}
+
 void AuthPacketCache::insert(DNSPacket *q, DNSPacket *r, unsigned int maxTTL)
 {
   cleanupIfNeeded();
@@ -132,6 +137,7 @@
   entry.qtype = q->qtype.getCode();
   entry.value = r->getString();
   entry.tcp = r->d_tcp;
+  entry.query = q->getString();
   
   auto& mc = getMap(entry.qname);
   {
@@ -146,8 +152,9 @@
     auto iter = range.first;
 
     for( ; iter != range.second ; ++iter)  {
-      if (iter->tcp != entry.tcp || iter->qtype != entry.qtype || iter->qname != entry.qname)
+      if (!entryMatches(iter, entry.query, entry.qname, entry.qtype, entry.tcp)) {
         continue;
+      }
 
       iter->value = entry.value;
       iter->ttd = now + ourttl;
@@ -161,7 +168,7 @@
   }
 }
 
-bool AuthPacketCache::getEntryLocked(cmap_t& map, uint32_t hash, const DNSName &qname, uint16_t qtype, bool tcp, time_t now, string& value)
+bool AuthPacketCache::getEntryLocked(cmap_t& map, const std::string& query, uint32_t hash, const DNSName &qname, uint16_t qtype, bool tcp, time_t now, string& value)
 {
   auto& idx = map.get<HashTag>();
   auto range = idx.equal_range(hash);
@@ -170,8 +177,9 @@
     if (iter->ttd < now)
       continue;
 
-    if (iter->tcp != tcp || iter->qtype != qtype || iter->qname != qname)
+    if (!entryMatches(iter, query, qname, qtype, tcp)) {
       continue;
+    }
 
     value = iter->value;
     return true;
diff -ru pdns-4.1.4.orig/pdns/auth-packetcache.hh pdns-4.1.4/pdns/auth-packetcache.hh
--- pdns-4.1.4.orig/pdns/auth-packetcache.hh	2018-08-29 16:07:04.000000000 +0200
+++ pdns-4.1.4/pdns/auth-packetcache.hh	2018-10-09 16:21:32.301227673 +0200
@@ -75,6 +75,7 @@
 
   struct CacheEntry
   {
+    mutable string query;
     mutable string value;
     DNSName qname;
 
@@ -109,7 +110,8 @@
     return d_maps[name.hash() % d_maps.size()];
   }
 
-  bool getEntryLocked(cmap_t& map, uint32_t hash, const DNSName &qname, uint16_t qtype, bool tcp, time_t now, string& entry);
+  static bool entryMatches(cmap_t::index<HashTag>::type::iterator& iter, const std::string& query, const DNSName& qname, uint16_t qtype, bool tcp);
+  bool getEntryLocked(cmap_t& map, const std::string& query, uint32_t hash, const DNSName &qname, uint16_t qtype, bool tcp, time_t now, string& entry);
   void cleanupIfNeeded();
 
   AtomicCounter d_ops{0};
diff -ru pdns-4.1.4.orig/pdns/packetcache.hh pdns-4.1.4/pdns/packetcache.hh
--- pdns-4.1.4.orig/pdns/packetcache.hh	2018-08-29 16:07:05.000000000 +0200
+++ pdns-4.1.4/pdns/packetcache.hh	2018-10-09 15:44:57.646908242 +0200
@@ -28,13 +28,13 @@
 
 class PacketCache : public boost::noncopyable
 {
-protected:
-  static uint32_t canHashPacket(const std::string& packet, bool skipECS=true)
+public:
+  static uint32_t canHashPacket(const std::string& packet, uint16_t* ecsBegin, uint16_t* ecsEnd)
   {
     uint32_t ret = 0;
-    ret=burtle((const unsigned char*)packet.c_str() + 2, 10, ret); // rest of dnsheader, skip id
+    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 = 12;
+    size_t pos = sizeof(dnsheader);
     const char* end = packet.c_str() + packetSize;
     const char* p = packet.c_str() + pos;
 
@@ -43,36 +43,110 @@
       ret=burtle(&l, 1, ret);
     }                           // XXX the embedded 0 in the qname will break the subnet stripping
 
-    struct dnsheader* dh = (struct dnsheader*)packet.c_str();
+    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(skipECS && ntohs(dh->arcount)==1 && (pos+16) < packetSize) {
+    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((char*) p + 14, end - (p + 14), EDNSOptionCode::ECS, &optionBegin, &optionLen);
+      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) {
-      // cerr << "Hashing from " << (p-packet.c_str()) << " for " << skipBegin-p << "bytes, end is at "<< end-packet.c_str() << endl;
-      ret = burtle((const unsigned char*)p, skipBegin-p, ret);
+      ret = burtle(reinterpret_cast<const unsigned char*>(p), skipBegin-p, ret);
     }
     if (skipEnd < end) {
-      // cerr << "Hashing from " << (skipEnd-packet.c_str()) << " for " << end-skipEnd << "bytes, end is at " << end-packet.c_str() << endl;
-      ret = burtle((const unsigned char*) skipEnd, end-skipEnd, ret);
+      ret = burtle(reinterpret_cast<const unsigned char*>(skipEnd), end-skipEnd, ret);
     }
 
     return ret;
   }
+
+  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;
+  }
+
+  static bool queryHeaderMatches(const std::string& cachedQuery, const std::string& query)
+  {
+    if (cachedQuery.size() != query.size()) {
+      return false;
+    }
+
+    return (cachedQuery.compare(/* skip the ID */ 2, sizeof(dnsheader) - 2, query, 2, sizeof(dnsheader) - 2) == 0);
+  }
+
+  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();
+
+    return (cachedQuery.compare(pos, cachedQuery.size() - pos, query, pos, query.size() - pos) == 0);
+  }
+
+  static bool queryMatches(const std::string& cachedQuery, const std::string& query, const DNSName& qname, uint16_t ecsBegin, uint16_t ecsEnd)
+  {
+    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;
+  }
+
 };
 
 #endif /* PACKETCACHE_HH */
