/*
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
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;
+ }
+}
/*
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
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
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);
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()) {
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;
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);
{
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);
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;