root/trunk/pdns/pdns/recursor_cache.cc @ 934

Revision 934, 12.2 KB (checked in by ahu, 6 years ago)

some more stats

  • Property svn:eol-style set to native
  • Property svn:keywords set to author date id revision
Line 
1#include "recursor_cache.hh"
2#include "misc.hh"
3#include <iostream>
4#include <boost/shared_ptr.hpp>
5#include "dnsrecords.hh"
6#include "arguments.hh"
7#include "syncres.hh"
8
9using namespace std;
10using namespace boost;
11
12#include "config.h"
13
14#ifdef GCC_SKIP_LOCKING
15#include <bits/atomicity.h>
16// This code is ugly but does speedup the recursor tremendously on multi-processor systems, and even has a large effect (20, 30%) on uniprocessor
17namespace __gnu_cxx
18{
19  _Atomic_word
20  __attribute__ ((__unused__))
21  __exchange_and_add(volatile _Atomic_word* __mem, int __val)
22  {
23    register _Atomic_word __result=*__mem;
24    *__mem+=__val;
25    return __result;
26  }
27
28  void
29  __attribute__ ((__unused__))
30  __atomic_add(volatile _Atomic_word* __mem, int __val)
31  {
32    *__mem+=__val;
33  }
34}
35#endif
36
37string simpleCompress(const string& label)
38{
39  typedef vector<pair<unsigned int, unsigned int> > parts_t;
40  parts_t parts;
41  vstringtok(parts, label, ".");
42  string ret;
43  ret.reserve(label.size()+4);
44  for(parts_t::const_iterator i=parts.begin(); i!=parts.end(); ++i) {
45    ret.append(1, (char)(i->second - i->first));
46    ret.append(label.c_str() + i->first, i->second - i->first);
47  }
48  ret.append(1, (char)0);
49  return ret;
50}
51
52void simpleExpandTo(const string& label, unsigned int frompos, string& ret)
53{
54  unsigned int labellen=0;
55  while((labellen=label.at(frompos++))) {
56    ret.append(label.c_str()+frompos, labellen);
57    ret.append(1,'.');
58    frompos+=labellen;
59  }
60}
61
62DNSResourceRecord String2DNSRR(const string& qname, const QType& qt, const string& serial, uint32_t ttd)
63{
64  DNSResourceRecord rr;
65  rr.ttl=ttd; 
66  rr.qtype=qt;
67  rr.qname=qname;
68
69  if(rr.qtype.getCode()==QType::A) {
70    uint32_t ip;
71    memcpy((char*)&ip, serial.c_str(), 4);
72    rr.content=U32ToIP(ntohl(ip));
73  }
74  else if(rr.qtype.getCode()==QType::CNAME || rr.qtype.getCode()==QType::NS || rr.qtype.getCode()==QType::PTR) {
75    unsigned int frompos=0;
76    unsigned char labellen;
77
78    while((labellen=serial.at(frompos++))) {
79      if((labellen & 0xc0) == 0xc0) {
80        string encoded=simpleCompress(qname);
81        uint16_t offset=256*(labellen & ~0xc0) + (unsigned int)serial.at(frompos++) - sizeof(dnsheader)-5;
82
83        simpleExpandTo(encoded, offset, rr.content);
84        //      cerr<<"Oops, fallback, content so far: '"<<rr.content<<"', offset: "<<offset<<", '"<<qname<<"', "<<qt.getName()<<"\n";
85        break;
86      }
87      rr.content.append(serial.c_str()+frompos, labellen);
88      frompos+=labellen;
89      rr.content.append(1,'.');
90    }
91    if(rr.content.empty())
92      rr.content=".";
93  }
94  else {
95    shared_ptr<DNSRecordContent> regen=DNSRecordContent::unserialize(qname, qt.getCode(), serial);
96    rr.content=regen->getZoneRepresentation();
97  }
98  rr.content.reserve(0);
99  rr.qname.reserve(0);
100  return rr;
101}
102
103string DNSRR2String(const DNSResourceRecord& rr)
104{
105  uint16_t type=rr.qtype.getCode();
106
107  if(type==QType::A) {
108    uint32_t ip;
109    IpToU32(rr.content, &ip);
110    return string((char*)&ip, 4);
111  }
112  else if(type==QType::NS) {
113    NSRecordContent ar(rr.content);
114    return ar.serialize(rr.qname);
115  }
116  else if(type==QType::CNAME) {
117    CNAMERecordContent ar(rr.content);
118    return ar.serialize(rr.qname);
119  }
120  else {
121    string ret;
122    shared_ptr<DNSRecordContent> drc(DNSRecordContent::mastermake(type, 1, rr.content));
123    ret=drc->serialize(rr.qname);
124  //  cerr<<"stored '"<<rr.qname<<" '"<<rr.qtype.getName()<<"' '"<<rr.content<<"' as "<<ret.size()<<" bytes"<<endl;
125    return ret;
126  }
127}
128
129unsigned int MemRecursorCache::size()
130{
131  return (unsigned int)d_cache.size();
132}
133
134unsigned int MemRecursorCache::bytes()
135{
136  unsigned int ret=0;
137
138  for(cache_t::const_iterator i=d_cache.begin(); i!=d_cache.end(); ++i) {
139    ret+=(unsigned int)i->d_qname.length();
140    for(vector<StoredRecord>::const_iterator j=i->d_records.begin(); j!= i->d_records.end(); ++j)
141      ret+=j->size();
142  }
143  return ret;
144}
145
146int MemRecursorCache::getDirect(time_t now, const char* qname, const QType& qt, uint32_t ttd[10], char* data[10], uint16_t len[10])
147{
148  if(!d_cachecachevalid || Utility::strcasecmp(d_cachedqname.c_str(), qname)) {
149//    cerr<<"had cache cache miss for '"<<qname<<"'"<<endl;
150    d_cachedqname=qname;
151    d_cachecache=d_cache.equal_range(tie(qname));
152    d_cachecachevalid=true;
153  }
154  else
155    ;
156  //    cerr<<"had cache cache hit!"<<endl;
157
158  if(d_cachecache.first == d_cachecache.second) {
159    g_stats.noShuntNoMatch++;
160    return false;
161  }
162
163  pair<cache_t::iterator, cache_t::iterator> range = d_cachecache;
164 
165  unsigned int n=0;
166  for(;range.first != range.second; ++range.first) {
167    if(range.first->d_qtype == QType::CNAME) { // if we see a cname, we need the whole shebang (for now)
168      g_stats.noShuntCNAME++;
169      return false;
170    }
171    if(range.first->d_qtype != qt.getCode())
172      continue;
173    if(range.first->getTTD() < (unsigned int) now) {
174      g_stats.noShuntExpired++;
175      return false;
176    }
177   
178    if(range.first->d_records.empty() || range.first->d_records.size() > 9 ) {
179      g_stats.noShuntSize++;
180      return false;
181    }
182   
183    size_t limit=range.first->d_records.size();
184    n=0;
185    for(; n < limit; ++n) {
186      data[n]=(char*)range.first->d_records[n].d_string.c_str();
187      len[n]=range.first->d_records[n].d_string.length();
188      ttd[n]=range.first->d_records[n].d_ttd;
189    }
190    if(n<10) {
191      data[n]=0;
192      typedef cache_t::nth_index<1>::type sequence_t;
193      sequence_t& sidx=d_cache.get<1>();
194      sequence_t::iterator si=d_cache.project<1>(range.first);
195      sidx.relocate(sidx.end(), si); // move it in the LRU list
196      // can't yet return, need to figure out if there isn't a CNAME that messes things up
197    }
198    else
199      return false;
200  }
201  if(!n)
202    g_stats.noShuntNoMatch++;
203  return n;
204
205}
206
207int MemRecursorCache::get(time_t now, const string &qname, const QType& qt, set<DNSResourceRecord>* res)
208{
209  unsigned int ttd=0;
210
211  //  cerr<<"looking up "<< qname+"|"+qt.getName()<<"\n";
212
213  if(!d_cachecachevalid || Utility::strcasecmp(d_cachedqname.c_str(), qname.c_str())) {
214    //    cerr<<"had cache cache miss"<<endl;
215    d_cachedqname=qname;
216    d_cachecache=d_cache.equal_range(tie(qname));
217    d_cachecachevalid=true;
218  }
219  else
220    ;
221  //    cerr<<"had cache cache hit!"<<endl;
222
223
224  if(res)
225    res->clear();
226
227  if(d_cachecache.first!=d_cachecache.second) { 
228    for(cache_t::const_iterator i=d_cachecache.first; i != d_cachecache.second; ++i) 
229      if(i->d_qtype == qt.getCode() || qt.getCode()==QType::ANY) {
230        typedef cache_t::nth_index<1>::type sequence_t;
231        sequence_t& sidx=d_cache.get<1>();
232        sequence_t::iterator si=d_cache.project<1>(i);
233       
234        for(vector<StoredRecord>::const_iterator k=i->d_records.begin(); k != i->d_records.end(); ++k) {
235          if(k->d_ttd < 1000000000 || k->d_ttd > (uint32_t) now) {
236            ttd=k->d_ttd;
237            if(res) {
238              DNSResourceRecord rr=String2DNSRR(qname, QType(i->d_qtype),  k->d_string, ttd); 
239              res->insert(rr);
240            }
241          }
242        }
243        if(res) {
244          if(res->empty())
245            sidx.relocate(sidx.begin(), si); 
246          else
247            sidx.relocate(sidx.end(), si); 
248        }
249        if(qt.getCode()!=QType::ANY) // normally if we have a hit, we are done
250          break;
251      }
252
253    //    cerr<<"time left : "<<ttd - now<<", "<< (res ? res->size() : 0) <<"\n";
254    return (unsigned int)ttd-now;
255  }
256  return -1;
257}
258 
259/* the code below is rather tricky - it basically replaces the stuff cached for qname by content, but it is special
260   cased for when inserting identical records with only differing ttls, in which case the entry is not
261   touched, but only given a new ttd */
262void MemRecursorCache::replace(time_t now, const string &qname, const QType& qt,  const set<DNSResourceRecord>& content, bool auth)
263{
264  d_cachecachevalid=false;
265  tuple<string, uint16_t> key=make_tuple(qname, qt.getCode());
266  cache_t::iterator stored=d_cache.find(key);
267
268  //  cerr<<"storing "<< qname+"|"+qt.getName()<<" -> '"<<content.begin()->content<<"'\n";
269
270  bool isNew=false;
271  if(stored == d_cache.end()) {
272    stored=d_cache.insert(CacheEntry(key,vector<StoredRecord>(), auth)).first;
273    isNew=true;
274  }
275 
276  pair<vector<StoredRecord>::iterator, vector<StoredRecord>::iterator> range;
277
278  StoredRecord dr;
279  CacheEntry ce=*stored;
280
281  if(qt.getCode()==QType::SOA || qt.getCode()==QType::CNAME)  // you can only have one (1) each of these
282    ce.d_records.clear();
283
284  if(auth && !ce.d_auth) {
285    ce.d_records.clear(); // clear non-auth data
286    ce.d_auth = true;
287    isNew=true;           // data should be sorted again
288  }
289
290  if(!auth && ce.d_auth) {  // unauth data came in, we have some auth data, but is it fresh?
291    vector<StoredRecord>::iterator j;
292    for(j = ce.d_records.begin() ; j != ce.d_records.end(); ++j) 
293      if((time_t)j->d_ttd > now) 
294        break;
295    if(j != ce.d_records.end()) { // we still have valid data, ignore unauth data
296      return;
297    }
298    else {
299      ce.d_auth = false;  // new data won't be auth
300    }
301  }
302
303  for(set<DNSResourceRecord>::const_iterator i=content.begin(); i != content.end(); ++i) {
304    dr.d_ttd=i->ttl;
305    dr.d_string=DNSRR2String(*i);
306   
307    if(isNew) 
308      ce.d_records.push_back(dr);
309    else {
310      range=equal_range(ce.d_records.begin(), ce.d_records.end(), dr);
311     
312      if(range.first != range.second) {
313        for(vector<StoredRecord>::iterator j=range.first ; j!=range.second; ++j) {
314          /* see http://mailman.powerdns.com/pipermail/pdns-users/2006-May/003413.html */
315          if(j->d_ttd > (unsigned int) now && i->ttl > j->d_ttd && qt.getCode()==QType::NS && auth) // don't allow auth servers to *raise* TTL of an NS record
316            continue;
317          if(i->ttl > j->d_ttd || (auth && d_followRFC2181) ) // authoritative packets can override the TTL to be lower
318            j->d_ttd=i->ttl;
319        }
320      }
321      else {
322        ce.d_records.push_back(dr);
323        sort(ce.d_records.begin(), ce.d_records.end());
324      }
325    }
326  }
327  if(isNew) {
328    sort(ce.d_records.begin(), ce.d_records.end());
329  }
330
331  if(ce.d_records.capacity() != ce.d_records.size())
332    vector<StoredRecord>(ce.d_records).swap(ce.d_records);
333
334  d_cache.replace(stored, ce);
335}
336
337int MemRecursorCache::doWipeCache(const string& name, uint16_t qtype)
338{
339  int count=0;
340  d_cachecachevalid=false;
341  pair<cache_t::iterator, cache_t::iterator> range;
342  if(qtype==0xffff)
343    range=d_cache.equal_range(tie(name));
344  else
345    range=d_cache.equal_range(tie(name, qtype));
346
347  for(cache_t::const_iterator i=range.first; i != range.second; ) {
348    count++;
349    d_cache.erase(i++);
350  }
351  return count;
352}
353
354void MemRecursorCache::doDumpAndClose(int fd)
355{
356  FILE* fp=fdopen(fd, "w");
357  if(!fp) {
358    close(fd);
359    return;
360  }
361
362  typedef cache_t::nth_index<1>::type sequence_t;
363  sequence_t& sidx=d_cache.get<1>();
364
365  time_t now=time(0);
366  for(sequence_t::const_iterator i=sidx.begin(); i != sidx.end(); ++i) {
367    for(vector<StoredRecord>::const_iterator j=i->d_records.begin(); j != i->d_records.end(); ++j) {
368      try {
369        DNSResourceRecord rr=String2DNSRR(i->d_qname, QType(i->d_qtype), j->d_string, j->d_ttd - now);
370        fprintf(fp, "%s %d IN %s %s\n", rr.qname.c_str(), rr.ttl, rr.qtype.getName().c_str(), rr.content.c_str());
371      }
372      catch(...) {
373        fprintf(fp, "; error printing '%s'\n", i->d_qname.c_str());
374      }
375    }
376  }
377  fclose(fp);
378}
379
380void MemRecursorCache::doSlash(int perc)
381{
382  doPrune();
383}
384
385void MemRecursorCache::doPrune(void)
386{
387  uint32_t now=(uint32_t)time(0);
388  d_cachecachevalid=false;
389
390  unsigned int maxCached=::arg().asNum("max-cache-entries");
391  unsigned int toTrim=0;
392 
393  unsigned int cacheSize=d_cache.size();
394
395  if(maxCached && cacheSize > maxCached) {
396    toTrim = cacheSize - maxCached;
397  }
398
399  //  cout<<"Need to trim "<<toTrim<<" from cache to meet target!\n";
400
401  typedef cache_t::nth_index<1>::type sequence_t;
402  sequence_t& sidx=d_cache.get<1>();
403
404  unsigned int tried=0, lookAt, erased=0;
405
406  // two modes - if toTrim is 0, just look through 10000 records and nuke everything that is expired
407  // otherwise, scan first 5*toTrim records, and stop once we've nuked enough
408  if(toTrim)
409    lookAt=5*toTrim;
410  else
411    lookAt=cacheSize/10;
412
413
414  sequence_t::iterator iter=sidx.begin(), eiter;
415  for(; iter != sidx.end() && tried < lookAt ; ++tried) {
416    unsigned int ttd=iter->getTTD();
417    if(ttd < now) { 
418      sidx.erase(iter++);
419      erased++;
420    }
421    else
422      ++iter;
423
424    if(toTrim && erased > toTrim)
425      break;
426  }
427
428  //  cout<<"erased "<<erased<<" records based on ttd\n";
429 
430  if(erased >= toTrim)
431    return;
432
433  //  if(toTrim)
434  //    cout<<"Still have "<<toTrim - erased<<" entries left to erase to meet target\n";
435
436
437  eiter=iter=sidx.begin();
438  std::advance(eiter, toTrim);
439  sidx.erase(iter, eiter);
440}
441
Note: See TracBrowser for help on using the browser.