]> granicus.if.org Git - pdns/commitdiff
make our question hash for query distribution case insensitive. add tests that check...
authorbert hubert <bert.hubert@netherlabs.nl>
Wed, 9 Sep 2015 09:17:45 +0000 (11:17 +0200)
committerbert hubert <bert.hubert@netherlabs.nl>
Wed, 9 Sep 2015 09:18:24 +0000 (11:18 +0200)
pdns/dns.cc
pdns/misc.cc
pdns/misc.hh
pdns/test-dnsname_cc.cc

index d790a7da483ccba0f9f5efb296b5746d42b8e1ef..2f66c5b10b1cf3b8961ca35569b94a4801fd1a57 100644 (file)
@@ -145,7 +145,7 @@ uint32_t hashQuestion(const char* packet, uint16_t len, uint32_t init)
   while((labellen=*pos++) && pos < end) { 
     if(pos + labellen + 1 > end) // include length field  in hash
       return 0;
-    ret=burtle(pos, labellen+1, ret);
+    ret=burtleCI(pos, labellen+1, ret);
     pos += labellen;
   }
   return ret;
index 1379315dcf8c052405347173c91b6cf32b8cc30f..bea87be2b1e2c42c6c4ee51458675356f3f55f00 100644 (file)
@@ -922,6 +922,47 @@ uint32_t burtle(const unsigned char* k, uint32_t length, uint32_t initval)
   return c;
 }
 
+uint32_t burtleCI(const unsigned char* k, uint32_t length, uint32_t initval)
+{
+  uint32_t a,b,c,len;
+
+   /* Set up the internal state */
+  len = length;
+  a = b = 0x9e3779b9;  /* the golden ratio; an arbitrary value */
+  c = initval;         /* the previous hash value */
+
+  /*---------------------------------------- handle most of the key */
+  while (len >= 12) {
+    a += (tolower(k[0]) +((uint32_t)tolower(k[1])<<8) +((uint32_t)tolower(k[2])<<16) +((uint32_t)tolower(k[3])<<24));
+    b += (tolower(k[4]) +((uint32_t)tolower(k[5])<<8) +((uint32_t)tolower(k[6])<<16) +((uint32_t)tolower(k[7])<<24));
+    c += (tolower(k[8]) +((uint32_t)tolower(k[9])<<8) +((uint32_t)tolower(k[10])<<16)+((uint32_t)tolower(k[11])<<24));
+    burtlemix(a,b,c);
+    k += 12; len -= 12;
+  }
+
+  /*------------------------------------- handle the last 11 bytes */
+  c += length;
+  switch(len) {             /* all the case statements fall through */
+  case 11: c+=((uint32_t)tolower(k[10])<<24);
+  case 10: c+=((uint32_t)tolower(k[9])<<16);
+  case 9 : c+=((uint32_t)tolower(k[8])<<8);
+    /* the first byte of c is reserved for the length */
+  case 8 : b+=((uint32_t)tolower(k[7])<<24);
+  case 7 : b+=((uint32_t)tolower(k[6])<<16);
+  case 6 : b+=((uint32_t)tolower(k[5])<<8);
+  case 5 : b+=tolower(k[4]);
+  case 4 : a+=((uint32_t)tolower(k[3])<<24);
+  case 3 : a+=((uint32_t)tolower(k[2])<<16);
+  case 2 : a+=((uint32_t)tolower(k[1])<<8);
+  case 1 : a+=tolower(k[0]);
+    /* case 0: nothing left to add */
+  }
+  burtlemix(a,b,c);
+  /*-------------------------------------------- report the result */
+  return c;
+}
+
+
 bool setSocketTimestamps(int fd)
 {
 #ifdef SO_TIMESTAMP
index 63816c8a88ed092938f86236cb14b60ca55c999a..0c6c8cd8ef9399a077322d811bea91c0034f1a20 100644 (file)
@@ -616,6 +616,7 @@ unsigned int getFilenumLimit(bool hardOrSoft=0);
 void setFilenumLimit(unsigned int lim);
 bool readFileIfThere(const char* fname, std::string* line);
 uint32_t burtle(const unsigned char* k, uint32_t lengh, uint32_t init);
+uint32_t burtleCI(const unsigned char* k, uint32_t lengh, uint32_t init);
 bool setSocketTimestamps(int fd);
 
 //! Sets the socket into blocking mode.
index 4c7f1e9db96abddae28421d95111baf2b83145ff..54dd369026772f06ef3a6b2c12f390753682069d 100644 (file)
@@ -209,6 +209,37 @@ BOOST_AUTO_TEST_CASE(test_Append) {
   BOOST_CHECK(dn == DNSName("www.powerdns.com."));
 }
 
+BOOST_AUTO_TEST_CASE(test_QuestionHash) {
+  vector<unsigned char> packet;
+  reportBasicTypes();
+  DNSPacketWriter dpw1(packet, "www.ds9a.nl.", QType::AAAA);
+  
+  auto hash1=hashQuestion((char*)&packet[0], packet.size(), 0);
+  DNSPacketWriter dpw2(packet, "wWw.Ds9A.nL.", QType::AAAA);
+  auto hash2=hashQuestion((char*)&packet[0], packet.size(), 0);
+  BOOST_CHECK_EQUAL(hash1, hash2);
+  vector<uint32_t> counts(1500);
+  for(unsigned int n=0; n < 100000; ++n) {
+    packet.clear();
+    DNSPacketWriter dpw1(packet, std::to_string(n)+"."+std::to_string(n*2)+".", QType::AAAA);
+    counts[hashQuestion((char*)&packet[0], packet.size(), 0) % counts.size()]++;
+  }
+  
+  double sum = std::accumulate(std::begin(counts), std::end(counts), 0.0);
+  double m =  sum / counts.size();
+  
+  double accum = 0.0;
+  std::for_each (std::begin(counts), std::end(counts), [&](const double d) {
+      accum += (d - m) * (d - m);
+  });
+      
+  double stdev = sqrt(accum / (counts.size()-1));
+  BOOST_CHECK(stdev < 10);      
+}
+  
+
 BOOST_AUTO_TEST_CASE(test_packetParse) {
   vector<unsigned char> packet;
   reportBasicTypes();