]> granicus.if.org Git - pdns/commitdiff
by popular demand, age responses from the packet cache. we try to be light, high...
authorBert Hubert <bert.hubert@netherlabs.nl>
Fri, 11 Jun 2010 06:46:21 +0000 (06:46 +0000)
committerBert Hubert <bert.hubert@netherlabs.nl>
Fri, 11 Jun 2010 06:46:21 +0000 (06:46 +0000)
git-svn-id: svn://svn.powerdns.com/pdns/trunk/pdns@1630 d19b8d6e-7fed-0310-83ef-9ca221ded41b

pdns/dnsparser.cc
pdns/dnsparser.hh
pdns/pdns_recursor.cc
pdns/recpacketcache.cc
pdns/recpacketcache.hh

index c467b92c4e780895ec3f57f333cfa66fb03b1fda..dff971c244cb3df78bc4bd8966431910d29581d1 100644 (file)
@@ -1,6 +1,6 @@
 /*
     PowerDNS Versatile Database Driven Nameserver
-    Copyright (C) 2005 - 2009  PowerDNS.COM BV
+    Copyright (C) 2005 - 2010  PowerDNS.COM BV
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License version 2 as 
@@ -511,3 +511,111 @@ void simpleExpandTo(const string& label, unsigned int frompos, string& ret)
     frompos+=labellen;
   }
 }
+
+/** Simple DNSPacketMangler. Ritual is: get a pointer into the packet and moveOffset() to beyond your needs
+ *  If you survive that, feel free to read from the pointer */
+class DNSPacketMangler
+{
+public:
+  explicit DNSPacketMangler(std::string& packet)
+    : d_packet(packet), d_notyouroffset(12), d_offset(d_notyouroffset)
+  {}
+  
+  void skipLabel()
+  {
+    uint8_t len; 
+    while((len=get8BitInt())) { 
+      if(len >= 0xc0) { // extended label
+       get8BitInt();
+       return;
+      }
+      skipBytes(len);
+    }
+  }
+  void skipBytes(uint16_t bytes)
+  {
+      moveOffset(bytes);
+  }
+  uint16_t get16BitInt()
+  {
+    const char* p = d_packet.c_str() + d_offset;
+    moveOffset(2);
+    uint16_t ret;
+    memcpy(&ret, (void*)p, 2);
+    return ntohs(ret);
+  }
+  
+  uint8_t get8BitInt()
+  {
+    const char* p = d_packet.c_str() + d_offset;
+    moveOffset(1);
+    return *p;
+  }
+  
+  void skipRData()
+  {
+    int toskip = get16BitInt();
+    moveOffset(toskip);
+  }
+  void decreaseAndSkip32BitInt(uint32_t decrease)
+  {
+    const char *p = (const char*)d_packet.c_str() + d_offset;
+    moveOffset(4);
+    
+    uint32_t tmp;
+    memcpy(&tmp, (void*) p, sizeof(tmp));
+    tmp = ntohl(tmp);
+    tmp-=decrease;
+    tmp = htonl(tmp);
+    d_packet.replace(d_offset-4, sizeof(tmp), (const char*)&tmp, sizeof(tmp));
+  }
+private:
+  void moveOffset(uint16_t by)
+  {
+    d_notyouroffset += by;
+    if(d_notyouroffset > d_packet.length())
+      throw range_error("dns packet out of range: "+lexical_cast<string>(d_notyouroffset) +" > " 
+      + lexical_cast<string>(d_packet.length()) );
+  }
+  std::string& d_packet;
+  
+  uint32_t d_notyouroffset;  // only 'moveOffset' can touch this
+  const uint32_t&  d_offset; // look.. but don't touch
+  
+};
+
+// method of operation: silently fail if it doesn't work - we're only trying to be nice, don't fall over on it
+void ageDNSPacket(std::string& packet, uint32_t seconds)
+{
+  if(packet.length() < 12)
+    return;
+  try 
+  {
+    const dnsheader* dh = (const dnsheader*)packet.c_str();
+    int numrecords = ntohs(dh->ancount) + ntohs(dh->nscount) + ntohs(dh->arcount);
+    DNSPacketMangler dpm(packet);
+    
+    int n;
+    for(n=0; n < ntohs(dh->qdcount) ; ++n) {
+      dpm.skipLabel();
+      dpm.skipBytes(4); // qtype, qclass
+    }
+   // cerr<<"Skipped "<<n<<" questions, now parsing "<<numrecords<<" records"<<endl;
+    for(n=0; n < numrecords; ++n) {
+      dpm.skipLabel();
+      
+      uint16_t dnstype = dpm.get16BitInt();
+      uint16_t dnsclass = dpm.get16BitInt();
+      
+      if(dnstype == QType::OPT) // not aging that one with a stick
+       break;
+      
+      dpm.decreaseAndSkip32BitInt(seconds);
+      dpm.skipRData();
+    }
+  }
+  catch(...)
+  {
+    return;
+  }
+}
index fdedb63c19dd46b7904aeeea3fbb60692808238f..c541a4aab4ee9ed409921fae680ed3f416c34cc2 100644 (file)
@@ -1,6 +1,6 @@
 /*
     PowerDNS Versatile Database Driven Nameserver
-    Copyright (C) 2005  PowerDNS.COM BV
+    Copyright (C) 2005 - 2010 PowerDNS.COM BV
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License version 2 as 
@@ -325,5 +325,5 @@ private:
 
 string simpleCompress(const string& label, const string& root="");
 void simpleExpandTo(const string& label, unsigned int frompos, string& ret);
-
+void ageDNSPacket(std::string& packet, uint32_t seconds);
 #endif
index 9bedfc3a9c25a42d352bc541ea0ea2cd2727e038..8e0ad23b5457429c12bad31dfb24ea77e5d1dd89 100644 (file)
@@ -826,12 +826,14 @@ void handleNewUDPQuestion(int fd, FDMultiplexer::funcparam_t& var)
 
         string response;
         try {
-          if(!SyncRes::s_nopacketcache && t_packetCache->getResponsePacket(string(data, len), g_now.tv_sec, &response)) {
+         uint32_t age;
+          if(!SyncRes::s_nopacketcache && t_packetCache->getResponsePacket(string(data, len), g_now.tv_sec, &response, &age)) {
             if(!g_quiet)
               L<<Logger::Error<<t_id<< " question answered from packet cache from "<<fromaddr.toString()<<endl;
   
             g_stats.packetCacheHits++;
             SyncRes::s_queries++;
+           ageDNSPacket(response, age);
             sendto(fd, response.c_str(), response.length(), 0, (struct sockaddr*) &fromaddr, fromaddr.getSocklen());
             if(response.length() >= sizeof(struct dnsheader))
               updateRcodeStats(((struct dnsheader*)response.c_str())->rcode);
index df040ee449b6dee14826549913611c036c92c812..99809297c5886df6cfcc0697758c179e2e3d87ed 100644 (file)
@@ -10,12 +10,12 @@ RecursorPacketCache::RecursorPacketCache()
   d_hits = d_misses = 0;
 }
 
-bool RecursorPacketCache::getResponsePacket(const std::string& queryPacket, time_t now, std::string* responsePacket)
+bool RecursorPacketCache::getResponsePacket(const std::string& queryPacket, time_t now, 
+  std::string* responsePacket, uint32_t* age)
 {
   struct Entry e;
   e.d_packet=queryPacket;
   
-  
   packetCache_t::const_iterator iter = d_packetCache.find(e);
   
   if(iter == d_packetCache.end()) {
@@ -27,6 +27,8 @@ bool RecursorPacketCache::getResponsePacket(const std::string& queryPacket, time
   sequence_t::iterator si=d_packetCache.project<1>(iter);
     
   if((uint32_t)now < iter->d_ttd) { // it is fresh!
+//    cerr<<"Fresh for another "<<iter->d_ttd - now<<" seconds!"<<endl;
+    *age = now - iter->d_creation;
     uint16_t id = ((struct dnsheader*)queryPacket.c_str())->id;
     *responsePacket = iter->d_packet;
     ((struct dnsheader*)responsePacket->c_str())->id=id;
@@ -46,11 +48,13 @@ void RecursorPacketCache::insertResponsePacket(const std::string& responsePacket
   struct Entry e;
   e.d_packet = responsePacket;
   e.d_ttd = now+ttl;
+  e.d_creation = now;
   packetCache_t::iterator iter = d_packetCache.find(e);
   
   if(iter != d_packetCache.end()) {
     iter->d_packet = responsePacket;
     iter->d_ttd = now + ttl;
+    iter->d_creation = now;
   }
   else 
     d_packetCache.insert(e);
index 320f84e44083386a455f4e66bfa2a04d923dbc40..5cf9fe4c52e6d4369071739eae37482a5bbe43d0 100644 (file)
@@ -19,7 +19,7 @@ class RecursorPacketCache
 {
 public:
   RecursorPacketCache();
-  bool getResponsePacket(const std::string& queryPacket, time_t now, std::string* responsePacket);
+  bool getResponsePacket(const std::string& queryPacket, time_t now, std::string* responsePacket, uint32_t* age);
   void insertResponsePacket(const std::string& responsePacket, time_t now, uint32_t ttd);
   void doPruneTo(unsigned int maxSize=250000);
   
@@ -32,6 +32,7 @@ private:
   struct Entry 
   {
     mutable uint32_t d_ttd;
+    mutable uint32_t d_creation;
     mutable std::string d_packet; // "I know what I am doing"
 
     inline bool operator<(const struct Entry& rhs) const;