~ubuntu-branches/ubuntu/maverick/pdns/maverick-updates

« back to all changes in this revision

Viewing changes to pdns/syncres.cc

  • Committer: Bazaar Package Importer
  • Author(s): Matthijs Mohlmann, Matthijs Mohlmann, Christoph Haas
  • Date: 2007-04-15 23:23:39 UTC
  • mfrom: (1.1.4 upstream)
  • Revision ID: james.westby@ubuntu.com-20070415232339-5x3scc8gx04e50um
Tags: 2.9.21-1
[ Matthijs Mohlmann ]
* New upstream release. (Closes: #420294)
* Remove meta pdns package.
* Added new sqlite3 backend package.
* Months and minutes where mixed up. (Closes: #406462)
* Case sensitivity in bind backend caused PowerDNS to not serve a certain
  zone. (Closes: #406461)
* Bind backend forgot about zones on a notify. (Closes: #398213)

[ Christoph Haas ]
* Documented incorporated backend bind. (Closes: #415471)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/*
2
2
    PowerDNS Versatile Database Driven Nameserver
3
 
    Copyright (C) 2003 - 2005  PowerDNS.COM BV
 
3
    Copyright (C) 2003 - 2006  PowerDNS.COM BV
4
4
 
5
5
    This program is free software; you can redistribute it and/or modify
6
6
    it under the terms of the GNU General Public License version 2 as published 
13
13
 
14
14
    You should have received a copy of the GNU General Public License
15
15
    along with this program; if not, write to the Free Software
16
 
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17
17
*/
18
18
 
 
19
#include <boost/algorithm/string.hpp>
19
20
#include "utility.hh"
20
21
#include "syncres.hh"
21
22
#include <iostream>
38
39
 
39
40
SyncRes::negcache_t SyncRes::s_negcache;    
40
41
SyncRes::nsspeeds_t SyncRes::s_nsSpeeds;    
41
 
 
 
42
unsigned int SyncRes::s_maxnegttl;
42
43
unsigned int SyncRes::s_queries;
43
44
unsigned int SyncRes::s_outgoingtimeouts;
44
45
unsigned int SyncRes::s_outqueries;
45
46
unsigned int SyncRes::s_tcpoutqueries;
46
47
unsigned int SyncRes::s_throttledqueries;
47
48
unsigned int SyncRes::s_nodelegated;
 
49
unsigned int SyncRes::s_unreachables;
 
50
bool SyncRes::s_doIPv6;
 
51
 
 
52
SyncRes::domainmap_t SyncRes::s_domainmap;
 
53
string SyncRes::s_serverID;
48
54
bool SyncRes::s_log;
49
55
 
50
56
#define LOG if(s_log) L<<Logger::Warning
51
57
 
52
 
Throttle<string> SyncRes::s_throttle;
 
58
SyncRes::throttle_t SyncRes::s_throttle;
53
59
 
54
60
/** everything begins here - this is the entry point just after receiving a packet */
55
 
int SyncRes::beginResolve(const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret)
 
61
int SyncRes::beginResolve(const string &qname, const QType &qtype, uint16_t qclass, vector<DNSResourceRecord>&ret)
56
62
{
57
 
  set<GetBestNSAnswer> beenthere;
58
63
  s_queries++;
59
 
  int res=doResolve(qname, qtype, ret,0,beenthere);
 
64
  if( (qtype.getCode()==QType::PTR && !Utility::strcasecmp(qname.c_str(), "1.0.0.127.in-addr.arpa.")) ||
 
65
      (qtype.getCode()==QType::A && qname.length()==10 && !Utility::strcasecmp(qname.c_str(), "localhost."))) {
 
66
    ret.clear();
 
67
    DNSResourceRecord rr;
 
68
    rr.qname=qname;
 
69
    rr.qtype=qtype;
 
70
    rr.qclass=1;
 
71
    rr.ttl=86400;
 
72
    if(qtype.getCode()==QType::PTR)
 
73
      rr.content="localhost.";
 
74
    else
 
75
      rr.content="127.0.0.1";
 
76
    ret.push_back(rr);
 
77
    return 0;
 
78
  }
 
79
 
 
80
  if(qclass==3 && qtype.getCode()==QType::TXT && 
 
81
        (!Utility::strcasecmp(qname.c_str(), "version.bind.") || !Utility::strcasecmp(qname.c_str(), "id.server.") || !Utility::strcasecmp(qname.c_str(), "version.pdns.") ) 
 
82
     ) {
 
83
    ret.clear();
 
84
    DNSResourceRecord rr;
 
85
    rr.qname=qname;
 
86
    rr.qtype=qtype;
 
87
    rr.qclass=qclass;
 
88
    rr.ttl=86400;
 
89
    if(!Utility::strcasecmp(qname.c_str(),"version.bind.")  || !Utility::strcasecmp(qname.c_str(),"version.pdns."))
 
90
      rr.content="\""+::arg()["version-string"]+"\"";
 
91
    else
 
92
      rr.content="\""+s_serverID+"\"";
 
93
    ret.push_back(rr);
 
94
    return 0;
 
95
  }
 
96
  
 
97
  if(qclass==0xff)
 
98
    qclass=1;
 
99
  else if(qclass!=1)
 
100
    return -1;
 
101
  
 
102
  set<GetBestNSAnswer> beenthere;
 
103
  int res=doResolve(qname, qtype, ret, 0, beenthere);
60
104
  if(!res)
61
105
    addCruft(qname, ret);
62
106
  return res;
63
107
}
64
108
 
 
109
//! This is the 'out of band resolver', in other words, the authoritative server
 
110
bool SyncRes::doOOBResolve(const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret, int depth, int& res)
 
111
{
 
112
  string prefix;
 
113
  if(s_log) {
 
114
    prefix=d_prefix;
 
115
    prefix.append(depth, ' ');
 
116
  }
 
117
 
 
118
  LOG<<prefix<<qname<<": checking auth storage for '"<<qname<<"|"<<qtype.getName()<<"'"<<endl;
 
119
  string authdomain(qname);
 
120
 
 
121
  domainmap_t::const_iterator iter=getBestAuthZone(&authdomain);
 
122
  if(iter==s_domainmap.end()) {
 
123
    LOG<<prefix<<qname<<": auth storage has no zone for this query!"<<endl;
 
124
    return false;
 
125
  }
 
126
  LOG<<prefix<<qname<<": auth storage has data, zone='"<<authdomain<<"'"<<endl;
 
127
  pair<AuthDomain::records_t::const_iterator, AuthDomain::records_t::const_iterator> range;
 
128
 
 
129
  range=iter->second.d_records.equal_range(tie(qname)); // partial lookup
 
130
  
 
131
  ret.clear();
 
132
  AuthDomain::records_t::const_iterator ziter;
 
133
  bool somedata=false;
 
134
  for(ziter=range.first; ziter!=range.second; ++ziter) {
 
135
    somedata=true;
 
136
    if(qtype.getCode()==QType::ANY || ziter->qtype==qtype || ziter->qtype.getCode()==QType::CNAME)  // let rest of nameserver do the legwork on this one
 
137
      ret.push_back(*ziter);
 
138
  }
 
139
  if(!ret.empty()) {
 
140
    LOG<<prefix<<qname<<": exact match in zone '"<<authdomain<<"'"<<endl;
 
141
    res=0;
 
142
    return true;
 
143
  }
 
144
  if(somedata) {
 
145
    LOG<<prefix<<qname<<": found record in '"<<authdomain<<"', but nothing of the right type, sending SOA"<<endl;
 
146
    ziter=iter->second.d_records.find(make_tuple(authdomain, QType(QType::SOA)));
 
147
    if(ziter!=iter->second.d_records.end()) {
 
148
      DNSResourceRecord rr=*ziter;
 
149
      rr.d_place=DNSResourceRecord::AUTHORITY;
 
150
      ret.push_back(rr);
 
151
    }
 
152
    else
 
153
      LOG<<prefix<<qname<<": can't find SOA record '"<<authdomain<<"' in our zone!"<<endl;
 
154
    res=RCode::NoError;
 
155
    return true;
 
156
  }
 
157
 
 
158
  string nsdomain(qname);
 
159
 
 
160
  while(chopOffDotted(nsdomain) && nsdomain!=iter->first) {
 
161
    range=iter->second.d_records.equal_range(make_tuple(nsdomain,QType(QType::NS))); 
 
162
    if(range.first==range.second)
 
163
      continue;
 
164
 
 
165
    for(ziter=range.first; ziter!=range.second; ++ziter) {
 
166
      DNSResourceRecord rr=*ziter;
 
167
      rr.d_place=DNSResourceRecord::AUTHORITY;
 
168
      ret.push_back(rr);
 
169
    }
 
170
  }
 
171
  if(ret.empty()) {
 
172
    LOG<<prefix<<qname<<": no NS match in zone '"<<authdomain<<"' either, handing out SOA"<<endl;
 
173
    ziter=iter->second.d_records.find(make_tuple(authdomain, QType(QType::SOA)));
 
174
    if(ziter!=iter->second.d_records.end()) {
 
175
      DNSResourceRecord rr=*ziter;
 
176
      rr.d_place=DNSResourceRecord::AUTHORITY;
 
177
      ret.push_back(rr);
 
178
    }
 
179
    else
 
180
      LOG<<prefix<<qname<<": can't find SOA record '"<<authdomain<<"' in our zone!"<<endl;
 
181
    res=RCode::NXDomain;
 
182
  }
 
183
  else 
 
184
    res=0;
 
185
 
 
186
  return true;
 
187
}
 
188
 
65
189
int SyncRes::doResolve(const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret, int depth, set<GetBestNSAnswer>& beenthere)
66
190
{
67
 
  string prefix(d_prefix);
68
 
  prefix.append(depth, ' ');
 
191
  string prefix;
 
192
  if(s_log) {
 
193
    prefix=d_prefix;
 
194
    prefix.append(depth, ' ');
 
195
  }
69
196
  
70
 
  int res;
71
 
  if(!(d_nocache && qtype.getCode()==QType::NS && qname.empty())) {
 
197
  int res=0;
 
198
  if(!(d_nocache && qtype.getCode()==QType::NS && qname==".")) {
 
199
    if(d_cacheonly) { // very limited OOB support
 
200
      LOG<<prefix<<qname<<": Recursion not requested for '"<<qname<<"|"<<qtype.getName()<<"', peeking at auth/forward zones"<<endl;
 
201
      string authname(qname);
 
202
      domainmap_t::const_iterator iter=getBestAuthZone(&authname);
 
203
      if(iter != s_domainmap.end()) {
 
204
        string server=iter->second.d_server;
 
205
        if(server.empty()) {
 
206
          LWRes::res_t result;
 
207
          ret.clear();
 
208
          doOOBResolve(qname, qtype, ret, depth, res);
 
209
          return res;
 
210
        }
 
211
        else {
 
212
          LOG<<prefix<<qname<<": forwarding query to hardcoded nameserver '"<<server<<"' for zone '"<<authname<<"'"<<endl;
 
213
          ComboAddress remoteIP(server, 53);
 
214
          res=d_lwr.asyncresolve(remoteIP, qname, qtype.getCode(), false, &d_now);    
 
215
          // filter out the good stuff from d_lwr.result()
 
216
          LWRes::res_t result=d_lwr.result();
 
217
 
 
218
          for(LWRes::res_t::const_iterator i=result.begin();i!=result.end();++i) {
 
219
            if(i->d_place == DNSResourceRecord::ANSWER)
 
220
              ret.push_back(*i);
 
221
          }
 
222
          return res;
 
223
        }
 
224
      }
 
225
    }
 
226
 
72
227
    if(doCNAMECacheCheck(qname,qtype,ret,depth,res)) // will reroute us if needed
73
228
      return res;
74
229
    
78
233
 
79
234
  if(d_cacheonly)
80
235
    return 0;
81
 
 
 
236
    
82
237
  LOG<<prefix<<qname<<": No cache hit for '"<<qname<<"|"<<qtype.getName()<<"', trying to find an appropriate NS record"<<endl;
83
238
 
84
239
  string subdomain(qname);
85
240
 
86
 
  set<string> nsset;
 
241
  set<string, CIStringCompare> nsset;
 
242
  bool flawedNSSet=false;
87
243
  for(int tries=0;tries<2 && nsset.empty();++tries) {
88
 
    subdomain=getBestNSNamesFromCache(subdomain,nsset,depth, beenthere); //  pass beenthere to both occasions
 
244
    subdomain=getBestNSNamesFromCache(subdomain, nsset, &flawedNSSet, depth, beenthere); //  pass beenthere to both occasions
89
245
 
90
246
    if(nsset.empty()) { // must've lost root records
91
247
      LOG<<prefix<<qname<<": our root expired, repriming from hints and retrying"<<endl;
93
249
    }
94
250
  }
95
251
 
96
 
  if(!(res=doResolveAt(nsset,subdomain,qname,qtype,ret,depth, beenthere)))
 
252
  if(!(res=doResolveAt(nsset, subdomain, flawedNSSet, qname, qtype, ret, depth, beenthere)))
97
253
    return 0;
98
254
  
99
 
  LOG<<prefix<<qname<<": failed"<<endl;
 
255
  LOG<<prefix<<qname<<": failed (res="<<res<<")"<<endl;
100
256
  return res<0 ? RCode::ServFail : res;
101
257
}
102
258
 
103
 
vector<string> SyncRes::getAs(const string &qname, int depth, set<GetBestNSAnswer>& beenthere)
 
259
/** This function explicitly goes out for A addresses, but if configured to use IPv6 as well, will also return any IPv6 addresses in the cache
 
260
    Additionally, it will return the 'best' address up front, and the rest shufled
 
261
*/
 
262
vector<ComboAddress> SyncRes::getAs(const string &qname, int depth, set<GetBestNSAnswer>& beenthere)
104
263
{
105
264
  typedef vector<DNSResourceRecord> res_t;
106
265
  res_t res;
107
266
 
108
 
  vector<string> ret;
 
267
  typedef vector<ComboAddress> ret_t;
 
268
  ret_t ret;
109
269
 
110
270
  if(!doResolve(qname,QType(QType::A), res,depth+1,beenthere) && !res.empty()) {
111
 
    for(res_t::const_iterator i=res.begin(); i!= res.end(); ++i)
112
 
      if(i->qtype.getCode()==QType::A)
113
 
        ret.push_back(i->content);
114
 
  }
115
 
  if(ret.size() > 1)
 
271
    for(res_t::const_iterator i=res.begin(); i!= res.end(); ++i) {
 
272
      if(i->qtype.getCode()==QType::A) {
 
273
        ret.push_back(ComboAddress(i->content, 53));
 
274
      }
 
275
    }
 
276
  }
 
277
 
 
278
  if(s_doIPv6) {
 
279
    typedef set<DNSResourceRecord> ipv6_t;
 
280
    ipv6_t ipv6;
 
281
    if(RC.get(d_now.tv_sec, qname, QType(QType::AAAA), &ipv6) > 0) {
 
282
      for(ipv6_t::const_iterator i=ipv6.begin(); i != ipv6.end(); ++i) 
 
283
        ret.push_back(ComboAddress(i->content, 53));
 
284
    }
 
285
  }
 
286
  
 
287
  if(ret.size() > 1) {
116
288
    random_shuffle(ret.begin(), ret.end());
 
289
 
 
290
    // move 'best' address up front
 
291
    nsspeeds_t::iterator best=s_nsSpeeds.find(qname);
 
292
 
 
293
    if(best != s_nsSpeeds.end())
 
294
      for(ret_t::iterator i=ret.begin(); i != ret.end(); ++i) {
 
295
        //      cerr<<"Is "<<i->toString()<<" equal to "<<best->second.d_best.toString()<<"?\n";
 
296
        if(*i==best->second.d_best) {
 
297
          if(i!=ret.begin()) {
 
298
            //      cerr<<"Moving "<<best->second.d_best.toString()<<" up front!\n";
 
299
            *i=*ret.begin();
 
300
            *ret.begin()=best->second.d_best;
 
301
          }
 
302
          break;
 
303
        }
 
304
      }
 
305
  }
 
306
    
117
307
  return ret;
118
308
}
119
309
 
120
 
void SyncRes::getBestNSFromCache(const string &qname, set<DNSResourceRecord>&bestns, int depth, set<GetBestNSAnswer>& beenthere)
 
310
void SyncRes::getBestNSFromCache(const string &qname, set<DNSResourceRecord>&bestns, bool* flawedNSSet, int depth, set<GetBestNSAnswer>& beenthere)
121
311
{
122
 
  string prefix(d_prefix), subdomain(qname);
123
 
  prefix.append(depth, ' ');
 
312
  string prefix, subdomain(qname);
 
313
  if(s_log) {
 
314
    prefix=d_prefix;
 
315
    prefix.append(depth, ' ');
 
316
  }
124
317
  bestns.clear();
125
318
 
126
319
  do {
127
320
    LOG<<prefix<<qname<<": Checking if we have NS in cache for '"<<subdomain<<"'"<<endl;
128
 
    set<DNSResourceRecord>ns;
129
 
    if(RC.get(d_now.tv_sec, subdomain, QType(QType::NS), &ns)>0) {
130
 
 
 
321
    set<DNSResourceRecord> ns;
 
322
    *flawedNSSet = false;
 
323
    if(RC.get(d_now.tv_sec, subdomain, QType(QType::NS), &ns) > 0) {
131
324
      for(set<DNSResourceRecord>::const_iterator k=ns.begin();k!=ns.end();++k) {
132
325
        if(k->ttl > (unsigned int)d_now.tv_sec ) { 
133
326
          set<DNSResourceRecord>aset;
134
327
 
135
328
          DNSResourceRecord rr=*k;
136
 
          rr.content=toLowerCanonic(k->content);
137
 
          if(!endsOn(rr.content,subdomain) || RC.get(d_now.tv_sec, rr.content ,QType(QType::A),&aset) > 5) {
 
329
          rr.content=k->content;
 
330
          if(!dottedEndsOn(rr.content, subdomain) || RC.get(d_now.tv_sec, rr.content, QType(QType::A), s_log ? &aset : 0) > 5) {
138
331
            bestns.insert(rr);
139
332
            
140
333
            LOG<<prefix<<qname<<": NS (with ip, or non-glue) in cache for '"<<subdomain<<"' -> '"<<rr.content<<"'"<<endl;
141
 
            LOG<<prefix<<qname<<": within bailiwick: "<<endsOn(rr.content,subdomain);
 
334
            LOG<<prefix<<qname<<": within bailiwick: "<<dottedEndsOn(rr.content, subdomain);
142
335
            if(!aset.empty()) {
143
336
              LOG<<", in cache, ttl="<<(unsigned int)(((time_t)aset.begin()->ttl- d_now.tv_sec ))<<endl;
144
337
            }
145
338
            else {
146
 
              LOG<<", not in cache"<<endl;
 
339
              LOG<<", not in cache / did not look at cache"<<endl;
147
340
            }
148
341
          }
149
 
          else
150
 
            LOG<<prefix<<qname<<": NS in cache for '"<<subdomain<<"', but needs glue ("<<toLowerCanonic(k->content)<<") which we miss or is expired"<<endl;
 
342
          else {
 
343
            *flawedNSSet=true;
 
344
            LOG<<prefix<<qname<<": NS in cache for '"<<subdomain<<"', but needs glue ("<<k->content<<") which we miss or is expired"<<endl;
 
345
          }
151
346
        }
152
347
      }
153
348
      if(!bestns.empty()) {
154
349
        GetBestNSAnswer answer;
155
 
        answer.qname=toLower(qname); answer.bestns=bestns;
 
350
        answer.qname=qname; answer.bestns=bestns;
156
351
        if(beenthere.count(answer)) {
157
352
          LOG<<prefix<<qname<<": We have NS in cache for '"<<subdomain<<"' but part of LOOP! Trying less specific NS"<<endl;
158
 
          for( set<GetBestNSAnswer>::const_iterator j=beenthere.begin();j!=beenthere.end();++j)
159
 
            LOG<<prefix<<qname<<": beenthere: "<<j->qname<<" ("<<j->bestns.size()<<")"<<endl;
 
353
          if(s_log)
 
354
            for( set<GetBestNSAnswer>::const_iterator j=beenthere.begin();j!=beenthere.end();++j)
 
355
              LOG<<prefix<<qname<<": beenthere: "<<j->qname<<" ("<<(unsigned int)j->bestns.size()<<")"<<endl;
160
356
          bestns.clear();
161
357
        }
162
358
        else {
163
359
          beenthere.insert(answer);
164
 
          LOG<<prefix<<qname<<": We have NS in cache for '"<<subdomain<<"'"<<endl;
 
360
          LOG<<prefix<<qname<<": We have NS in cache for '"<<subdomain<<"' (flawedNSSet="<<*flawedNSSet<<")"<<endl;
165
361
          return;
166
362
        }
167
363
      }
168
364
    }
169
 
  }while(chopOff(subdomain));
 
365
    LOG<<prefix<<qname<<": no valid/useful NS in cache for '"<<subdomain<<"'"<<endl;
 
366
  }while(chopOffDotted(subdomain));
170
367
}
171
368
 
 
369
SyncRes::domainmap_t::const_iterator SyncRes::getBestAuthZone(string* qname)
 
370
{
 
371
  SyncRes::domainmap_t::const_iterator ret;
 
372
  do {
 
373
    ret=s_domainmap.find(*qname);
 
374
    if(ret!=s_domainmap.end()) 
 
375
      break;
 
376
  }while(chopOffDotted(*qname));
 
377
  return ret;
 
378
}
172
379
 
173
380
/** doesn't actually do the work, leaves that to getBestNSFromCache */
174
 
string SyncRes::getBestNSNamesFromCache(const string &qname,set<string>& nsset, int depth, set<GetBestNSAnswer>&beenthere)
 
381
string SyncRes::getBestNSNamesFromCache(const string &qname, set<string, CIStringCompare>& nsset, bool* flawedNSSet, int depth, set<GetBestNSAnswer>&beenthere)
175
382
{
176
383
  string subdomain(qname);
177
384
 
 
385
  string authdomain(qname);
 
386
  
 
387
  domainmap_t::const_iterator iter=getBestAuthZone(&authdomain);
 
388
  if(iter!=s_domainmap.end()) {
 
389
    nsset.insert(iter->second.d_server); // this gets picked up in doResolveAt, if empty it means "we are auth", otherwise it denotes a forward
 
390
    return authdomain;
 
391
  }
 
392
 
178
393
  set<DNSResourceRecord> bestns;
179
 
  getBestNSFromCache(subdomain, bestns, depth, beenthere);
 
394
  getBestNSFromCache(subdomain, bestns, flawedNSSet, depth, beenthere);
180
395
 
181
396
  for(set<DNSResourceRecord>::const_iterator k=bestns.begin();k!=bestns.end();++k) {
182
397
    nsset.insert(k->content);
183
 
    subdomain=k->qname;
 
398
    if(k==bestns.begin())
 
399
      subdomain=k->qname;
184
400
  }
185
401
  return subdomain;
186
402
}
187
403
 
188
404
bool SyncRes::doCNAMECacheCheck(const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret, int depth, int &res)
189
405
{
190
 
  string prefix(d_prefix), tuple=toLowerCanonic(qname)+"|CNAME";
191
 
  prefix.append(depth, ' ');
 
406
  string prefix;
 
407
  if(s_log) {
 
408
    prefix=d_prefix; 
 
409
    prefix.append(depth, ' ');
 
410
  }
192
411
 
193
412
  if(depth>10) {
194
413
    LOG<<prefix<<qname<<": CNAME loop too deep, depth="<<depth<<endl;
196
415
    return true;
197
416
  }
198
417
  
199
 
  LOG<<prefix<<qname<<": Looking for CNAME cache hit of '"<<tuple<<"'"<<endl;
 
418
  LOG<<prefix<<qname<<": Looking for CNAME cache hit of '"<<(qname+"|CNAME")<<"'"<<endl;
200
419
  set<DNSResourceRecord> cset;
201
420
  if(RC.get(d_now.tv_sec, qname,QType(QType::CNAME),&cset) > 0) {
202
421
 
203
422
    for(set<DNSResourceRecord>::const_iterator j=cset.begin();j!=cset.end();++j) {
204
423
      if(j->ttl>(unsigned int) d_now.tv_sec) {
205
 
        LOG<<prefix<<qname<<": Found cache CNAME hit for '"<<tuple<<"' to '"<<j->content<<"'"<<endl;    
 
424
        LOG<<prefix<<qname<<": Found cache CNAME hit for '"<< (qname+"|CNAME") <<"' to '"<<j->content<<"'"<<endl;    
206
425
        DNSResourceRecord rr=*j;
207
426
        rr.ttl-=d_now.tv_sec;
208
427
        ret.push_back(rr);
209
428
        if(!(qtype==QType(QType::CNAME))) { // perhaps they really wanted a CNAME!
210
429
          set<GetBestNSAnswer>beenthere;
211
 
          res=doResolve(toLowerCanonic(j->content), qtype, ret, depth+1, beenthere);
 
430
          res=doResolve(j->content, qtype, ret, depth+1, beenthere);
212
431
        }
213
432
        else
214
433
          res=0;
216
435
      }
217
436
    }
218
437
  }
219
 
  LOG<<prefix<<qname<<": No CNAME cache hit of '"<<tuple<<"' found"<<endl;
 
438
  LOG<<prefix<<qname<<": No CNAME cache hit of '"<< (qname+"|CNAME") <<"' found"<<endl;
220
439
  return false;
221
440
}
222
441
 
223
442
bool SyncRes::doCacheCheck(const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret, int depth, int &res)
224
443
{
225
444
  bool giveNegative=false;
226
 
  string prefix(d_prefix), tuple;
227
 
  prefix.append(depth, ' ');
 
445
  
 
446
  string prefix;
 
447
  if(s_log) {
 
448
    prefix=d_prefix;
 
449
    prefix.append(depth, ' ');
 
450
  }
228
451
 
229
452
  string sqname(qname);
230
453
  QType sqt(qtype);
231
454
  uint32_t sttl=0;
232
 
 
233
 
  if(s_negcache.count(toLower(qname))) {
234
 
    res=0;
235
 
    negcache_t::const_iterator ni=s_negcache.find(toLower(qname));
236
 
    if(d_now.tv_sec < ni->second.ttd) {
237
 
      sttl=ni->second.ttd - d_now.tv_sec;
238
 
      LOG<<prefix<<qname<<": Entire record '"<<toLower(qname)<<"', is negatively cached for another "<<sttl<<" seconds"<<endl;
239
 
      res=RCode::NXDomain; 
240
 
      giveNegative=true;
241
 
      sqname=ni->second.name;
242
 
      sqt="SOA";
243
 
    }
244
 
    else {
245
 
      LOG<<prefix<<qname<<": Entire record '"<<toLower(qname)<<"' was negatively cached, but entry expired"<<endl;
246
 
      s_negcache.erase(toLower(qname));
247
 
    }
248
 
  }
249
 
 
250
 
  if(!giveNegative) { // let's try some more
251
 
    tuple=toLower(qname); tuple.append(1,'|'); tuple+=qtype.getName();
252
 
    LOG<<prefix<<qname<<": Looking for direct cache hit of '"<<tuple<<"', negative cached: "<<s_negcache.count(tuple)<<endl;
253
 
 
254
 
    res=0;
255
 
    negcache_t::const_iterator ni=s_negcache.find(tuple);
256
 
    if(ni!=s_negcache.end()) {
257
 
      if(d_now.tv_sec < ni->second.ttd) {
258
 
        sttl=ni->second.ttd - d_now.tv_sec;
259
 
        LOG<<prefix<<qname<<": "<<qtype.getName()<<" is negatively cached for another "<<sttl<<" seconds"<<endl;
260
 
        res=RCode::NoError; // only this record doesn't exist
 
455
  //  cout<<"Lookup for '"<<qname<<"|"<<qtype.getName()<<"'\n";
 
456
 
 
457
  pair<negcache_t::const_iterator, negcache_t::const_iterator> range=s_negcache.equal_range(tie(qname));
 
458
  negcache_t::iterator ni;
 
459
  for(ni=range.first; ni != range.second; ni++) {
 
460
    // we have something
 
461
    if(ni->d_qtype.getCode() == 0 || ni->d_qtype == qtype) {
 
462
      res=0;
 
463
      if((uint32_t)d_now.tv_sec < ni->d_ttd) {
 
464
        sttl=ni->d_ttd - d_now.tv_sec;
 
465
        if(ni->d_qtype.getCode()) {
 
466
          LOG<<prefix<<qname<<": "<<qtype.getName()<<" is negatively cached via '"<<ni->d_qname<<"' for another "<<sttl<<" seconds"<<endl;
 
467
          res = RCode::NoError;
 
468
        }
 
469
        else {
 
470
          LOG<<prefix<<qname<<": Entire record '"<<qname<<"', is negatively cached via '"<<ni->d_qname<<"' for another "<<sttl<<" seconds"<<endl;
 
471
          res= RCode::NXDomain; 
 
472
        }
261
473
        giveNegative=true;
262
 
        sqname=ni->second.name;
263
 
        sqt="SOA";
 
474
        sqname=ni->d_qname;
 
475
        sqt=QType::SOA;
 
476
        break;
264
477
      }
265
478
      else {
266
 
        LOG<<prefix<<qname<<": "<<qtype.getName()<<" was negatively cached, but entry expired"<<endl;
267
 
        s_negcache.erase(toLower(tuple));
 
479
        LOG<<prefix<<qname<<": Entire record '"<<qname<<"' was negatively cached, but entry expired"<<endl;
268
480
      }
269
481
    }
270
482
  }
271
483
 
272
484
  set<DNSResourceRecord> cset;
273
485
  bool found=false, expired=false;
274
 
  if(RC.get(d_now.tv_sec, sqname,sqt,&cset)>0) {
275
 
    LOG<<prefix<<qname<<": Found cache hit for "<<sqt.getName()<<": ";
 
486
 
 
487
  if(RC.get(d_now.tv_sec, sqname, sqt, &cset) > 0) {
 
488
    LOG<<prefix<<sqname<<": Found cache hit for "<<sqt.getName()<<": ";
276
489
    for(set<DNSResourceRecord>::const_iterator j=cset.begin();j!=cset.end();++j) {
277
490
      LOG<<j->content;
278
491
      if(j->ttl>(unsigned int) d_now.tv_sec) {
293
506
    }
294
507
  
295
508
    LOG<<endl;
296
 
    if(found && !expired) 
 
509
    if(found && !expired) {
 
510
      if(!giveNegative)
 
511
        res=0;
297
512
      return true;
 
513
    }
298
514
    else
299
515
      LOG<<prefix<<qname<<": cache had only stale entries"<<endl;
300
516
  }
 
517
 
301
518
  return false;
302
519
}
303
520
 
304
521
bool SyncRes::moreSpecificThan(const string& a, const string &b)
305
522
{
306
 
  int counta=!a.empty(), countb=!b.empty();
 
523
  static string dot(".");
 
524
  int counta=(a!=dot), countb=(b!=dot);
307
525
  
308
526
  for(string::size_type n=0;n<a.size();++n)
309
527
    if(a[n]=='.')
314
532
  return counta>countb;
315
533
}
316
534
 
317
 
 
318
 
 
319
535
struct speedOrder
320
536
{
321
537
  speedOrder(map<string,double> &speeds) : d_speeds(speeds) {}
326
542
  map<string,double>& d_speeds;
327
543
};
328
544
 
329
 
inline vector<string> SyncRes::shuffle(set<string> &nameservers, const string &prefix)
 
545
inline vector<string> SyncRes::shuffleInSpeedOrder(set<string, CIStringCompare> &nameservers, const string &prefix)
330
546
{
331
547
  vector<string> rnameservers;
332
548
  rnameservers.reserve(nameservers.size());
333
549
  map<string,double> speeds;
334
550
 
335
 
  for(set<string>::const_iterator i=nameservers.begin();i!=nameservers.end();++i) {
 
551
  for(set<string, CIStringCompare>::const_iterator i=nameservers.begin();i!=nameservers.end();++i) {
336
552
    rnameservers.push_back(*i);
337
 
    DecayingEwma& temp=s_nsSpeeds[toLower(*i)];
338
 
    speeds[*i]=temp.get(&d_now);
 
553
    speeds[*i]=s_nsSpeeds[*i].get(&d_now);
339
554
  }
340
555
  random_shuffle(rnameservers.begin(),rnameservers.end());
341
 
  stable_sort(rnameservers.begin(),rnameservers.end(),speedOrder(speeds));
 
556
  speedOrder so(speeds);
 
557
  stable_sort(rnameservers.begin(),rnameservers.end(), so);
342
558
  
343
559
  if(s_log) {
344
560
    L<<Logger::Warning<<prefix<<"Nameservers: ";
345
561
    for(vector<string>::const_iterator i=rnameservers.begin();i!=rnameservers.end();++i) {
346
562
      if(i!=rnameservers.begin()) {
347
563
        L<<", ";
348
 
        if(!((i-rnameservers.begin())%4))
 
564
        if(!((i-rnameservers.begin())%3))
349
565
          L<<endl<<Logger::Warning<<prefix<<"             ";
350
566
      }
351
567
      L<<*i<<"(" << (int)(speeds[*i]/1000.0) <<"ms)";
355
571
  return rnameservers;
356
572
}
357
573
 
 
574
struct TCacheComp
 
575
{
 
576
  bool operator()(const pair<string, QType>& a, const pair<string, QType>& b) const
 
577
  {
 
578
    int cmp=Utility::strcasecmp(a.first.c_str(), b.first.c_str());
 
579
    if(cmp < 0)
 
580
      return true;
 
581
    if(cmp > 0)
 
582
      return false;
 
583
 
 
584
    return a.second < b.second;
 
585
  }
 
586
};
 
587
 
 
588
 
 
589
 
358
590
/** returns -1 in case of no results, rcode otherwise */
359
 
int SyncRes::doResolveAt(set<string> nameservers, string auth, const string &qname, const QType &qtype, vector<DNSResourceRecord>&ret, 
360
 
                int depth, set<GetBestNSAnswer>&beenthere)
 
591
int SyncRes::doResolveAt(set<string, CIStringCompare> nameservers, string auth, bool flawedNSSet, const string &qname, const QType &qtype, 
 
592
                         vector<DNSResourceRecord>&ret, 
 
593
                         int depth, set<GetBestNSAnswer>&beenthere)
361
594
{
362
 
  string prefix(d_prefix);
363
 
  prefix.append(depth, ' ');
 
595
  string prefix;
 
596
  if(s_log) {
 
597
    prefix=d_prefix;
 
598
    prefix.append(depth, ' ');
 
599
  }
364
600
  
365
601
  LWRes::res_t result;
366
602
 
367
 
  LOG<<prefix<<qname<<": Cache consultations done, have "<<nameservers.size()<<" NS to contact"<<endl;
 
603
  LOG<<prefix<<qname<<": Cache consultations done, have "<<(unsigned int)nameservers.size()<<" NS to contact"<<endl;
368
604
 
369
605
  for(;;) { // we may get more specific nameservers
370
606
    result.clear();
371
607
 
372
 
    vector<string> rnameservers=shuffle(nameservers, prefix+qname+": ");
 
608
    vector<string> rnameservers=shuffleInSpeedOrder(nameservers, s_log ? (prefix+qname+": ") : string() );
373
609
 
374
610
    for(vector<string>::const_iterator tns=rnameservers.begin();;++tns) { 
375
611
      if(tns==rnameservers.end()) {
376
 
        LOG<<prefix<<qname<<": Failed to resolve via any of the "<<rnameservers.size()<<" offered NS"<<endl;
 
612
        LOG<<prefix<<qname<<": Failed to resolve via any of the "<<(unsigned int)rnameservers.size()<<" offered NS at level '"<<auth<<"'"<<endl;
 
613
        if(auth!="." && flawedNSSet) {
 
614
          g_stats.nsSetInvalidations++;
 
615
          LOG<<prefix<<qname<<": Invalidating nameservers for level '"<<auth<<"', next query might succeed"<<endl;
 
616
          RC.doWipeCache(auth, QType::NS);
 
617
        }
377
618
        return -1;
378
619
      }
379
620
      if(qname==*tns && qtype.getCode()==QType::A) {
380
621
        LOG<<prefix<<qname<<": Not using NS to resolve itself!"<<endl;
381
622
        continue;
382
623
      }
383
 
      LOG<<prefix<<qname<<": Trying to resolve NS "<<*tns<<" ("<<1+tns-rnameservers.begin()<<"/"<<rnameservers.size()<<")"<<endl;
384
 
      typedef vector<string> remoteIPs_t;
385
 
      remoteIPs_t remoteIPs=getAs(*tns, depth+1, beenthere);
386
 
      if(remoteIPs.empty()) {
387
 
        LOG<<prefix<<qname<<": Failed to get IP for NS "<<*tns<<", trying next if available"<<endl;
388
 
        continue;
389
 
      }
 
624
 
 
625
      typedef vector<ComboAddress> remoteIPs_t;
 
626
      remoteIPs_t remoteIPs;
390
627
      remoteIPs_t::const_iterator remoteIP;
391
628
      bool doTCP=false;
392
 
      for(remoteIP = remoteIPs.begin(); remoteIP != remoteIPs.end(); ++remoteIP) {
393
 
        LOG<<prefix<<qname<<": Resolved '"+auth+"' NS "<<*tns<<" to "<<*remoteIP<<", asking '"<<qname<<"|"<<qtype.getName()<<"'"<<endl;
394
 
        
395
 
        if(s_throttle.shouldThrottle(d_now.tv_sec, *remoteIP+"|"+qname+"|"+qtype.getName())) {
396
 
          LOG<<prefix<<qname<<": query throttled "<<endl;
397
 
          s_throttledqueries++;
398
 
          d_throttledqueries++;
 
629
      int resolveret;
 
630
 
 
631
      if(tns->empty()) {
 
632
        LOG<<prefix<<qname<<": Domain is out-of-band"<<endl;
 
633
        doOOBResolve(qname, qtype, result, depth, d_lwr.d_rcode);
 
634
        d_lwr.d_tcbit=false;
 
635
        d_lwr.d_aabit=true;
 
636
      }
 
637
      else {
 
638
        LOG<<prefix<<qname<<": Trying to resolve NS '"<<*tns<<"' ("<<1+tns-rnameservers.begin()<<"/"<<(unsigned int)rnameservers.size()<<")"<<endl;
 
639
        if(!isCanonical(*tns)) {
 
640
          LOG<<prefix<<qname<<": Domain has hardcoded nameserver"<<endl;
 
641
          remoteIPs.push_back(ComboAddress(*tns, 53));
 
642
        }
 
643
        else
 
644
          remoteIPs=getAs(*tns, depth+1, beenthere);
 
645
 
 
646
        if(remoteIPs.empty()) {
 
647
          LOG<<prefix<<qname<<": Failed to get IP for NS "<<*tns<<", trying next if available"<<endl;
 
648
          flawedNSSet=true;
399
649
          continue;
400
650
        }
401
651
        else {
402
 
          s_outqueries++;
403
 
          d_outqueries++;
404
 
        TryTCP:
405
 
          if(doTCP) {
406
 
            s_tcpoutqueries++;
407
 
            d_tcpoutqueries++;
408
 
          }
409
 
          
410
 
          int ret=d_lwr.asyncresolve(*remoteIP, qname.c_str(), qtype.getCode(), doTCP);    // <- we go out on the wire!
411
 
          if(ret != 1) {
412
 
            if(ret==0) {
413
 
              LOG<<prefix<<qname<<": timeout resolving"<<endl;
414
 
              d_timeouts++;
415
 
              s_outgoingtimeouts++;
416
 
            }
417
 
            else
418
 
              LOG<<prefix<<qname<<": error resolving"<<endl;
419
 
            
420
 
            s_nsSpeeds[toLower(*tns)].submit(1000000, &d_now); // 1 sec
421
 
          
422
 
            s_throttle.throttle(d_now.tv_sec, *remoteIP+"|"+qname+"|"+qtype.getName(),20,5);
423
 
            continue;
424
 
          }
425
 
          gettimeofday(&d_now, 0);
426
 
          break; // it did work!
427
 
        }
428
 
      }
429
 
 
430
 
      if(remoteIP == remoteIPs.end())  // we tried all IP addresses, none worked
431
 
        continue; 
432
 
 
433
 
      result=d_lwr.result();
 
652
          LOG<<prefix<<qname<<": Resolved '"+auth+"' NS "<<*tns<<" to: ";
 
653
          for(remoteIP = remoteIPs.begin(); remoteIP != remoteIPs.end(); ++remoteIP) {
 
654
            if(remoteIP != remoteIPs.begin())
 
655
              LOG<<", ";
 
656
            LOG<<remoteIP->toString();
 
657
          }
 
658
          LOG<<endl;
 
659
 
 
660
        }
 
661
        for(remoteIP = remoteIPs.begin(); remoteIP != remoteIPs.end(); ++remoteIP) {
 
662
          LOG<<prefix<<qname<<": Trying IP "<< remoteIP->toString() <<", asking '"<<qname<<"|"<<qtype.getName()<<"'"<<endl;
 
663
          extern NetmaskGroup* g_dontQuery;
 
664
          
 
665
          if(s_throttle.shouldThrottle(d_now.tv_sec, make_tuple(*remoteIP, qname, qtype.getCode()))) {
 
666
            LOG<<prefix<<qname<<": query throttled "<<endl;
 
667
            s_throttledqueries++; d_throttledqueries++;
 
668
            continue;
 
669
          } 
 
670
          else if(g_dontQuery && g_dontQuery->match(&*remoteIP)) {
 
671
            LOG<<prefix<<qname<<": not sending query to " << remoteIP->toString() << ", blocked by 'dont-query' setting" << endl;
 
672
            continue;
 
673
          }
 
674
          else {
 
675
            s_outqueries++; d_outqueries++;
 
676
          TryTCP:
 
677
            if(doTCP) {
 
678
              s_tcpoutqueries++; d_tcpoutqueries++;
 
679
            }
 
680
            
 
681
            resolveret=d_lwr.asyncresolve(*remoteIP, qname, qtype.getCode(), doTCP, &d_now);    // <- we go out on the wire!
 
682
            if(resolveret != 1) {
 
683
              if(resolveret==0) {
 
684
                LOG<<prefix<<qname<<": timeout resolving "<< (doTCP ? "over TCP" : "")<<endl;
 
685
                d_timeouts++;
 
686
                s_outgoingtimeouts++;
 
687
              }
 
688
              else if(resolveret==-2) {
 
689
                LOG<<prefix<<qname<<": hit a local resource limit resolving "<< (doTCP ? "over TCP" : "")<<endl;
 
690
                g_stats.resourceLimits++;
 
691
              }
 
692
              else {
 
693
                s_unreachables++; d_unreachables++;
 
694
                LOG<<prefix<<qname<<": error resolving "<< (doTCP ? "over TCP" : "") << endl;
 
695
              }
 
696
              
 
697
              if(resolveret!=-2) { // don't account for resource limits, they are our own fault
 
698
                s_nsSpeeds[*tns].submit(*remoteIP, 1000000, &d_now); // 1 sec
 
699
                if(resolveret==-1)
 
700
                  s_throttle.throttle(d_now.tv_sec, make_tuple(*remoteIP, qname, qtype.getCode()), 60, 100); // unreachable
 
701
                else
 
702
                  s_throttle.throttle(d_now.tv_sec, make_tuple(*remoteIP, qname, qtype.getCode()), 20, 5);  // timeout
 
703
              }
 
704
              continue;
 
705
            }
 
706
            
 
707
            break;  // this IP address worked!
 
708
          wasLame:; // well, it didn't
 
709
            LOG<<prefix<<qname<<": status=NS "<<*tns<<" ("<< remoteIP->toString() <<") is lame for '"<<auth<<"', trying sibling IP or NS"<<endl;
 
710
            s_throttle.throttle(d_now.tv_sec, make_tuple(*remoteIP, qname, qtype.getCode()), 60, 100);
 
711
          }
 
712
        }
 
713
        
 
714
        if(remoteIP == remoteIPs.end())  // we tried all IP addresses, none worked
 
715
          continue; 
 
716
        
 
717
        result=d_lwr.result();
434
718
      
435
 
      if(d_lwr.d_tcbit) {
436
 
        if(!doTCP) {
437
 
          doTCP=true;
438
 
          LOG<<prefix<<qname<<": truncated bit set, retrying via TCP"<<endl;
439
 
          goto TryTCP;
440
 
        }
441
 
        LOG<<prefix<<qname<<": truncated bit set, over TCP?"<<endl;
442
 
        return RCode::ServFail;
443
 
      }
444
 
 
445
 
      if(d_lwr.d_rcode==RCode::ServFail) {
446
 
        LOG<<prefix<<qname<<": "<<*tns<<" returned a ServFail, trying sibling NS"<<endl;
447
 
        s_throttle.throttle(d_now.tv_sec,*remoteIP+"|"+qname+"|"+qtype.getName(),60,3);
448
 
        continue;
449
 
      }
450
 
      LOG<<prefix<<qname<<": Got "<<result.size()<<" answers from "<<*tns<<" ("<<*remoteIP<<"), rcode="<<d_lwr.d_rcode<<", in "<<d_lwr.d_usec/1000<<"ms"<<endl;
451
 
      s_nsSpeeds[toLower(*tns)].submit(d_lwr.d_usec, &d_now);
452
 
 
453
 
      map<string,set<DNSResourceRecord> > tcache;
 
719
        if(d_lwr.d_tcbit) {
 
720
          if(!doTCP) {
 
721
            doTCP=true;
 
722
            LOG<<prefix<<qname<<": truncated bit set, retrying via TCP"<<endl;
 
723
            goto TryTCP;
 
724
          }
 
725
          LOG<<prefix<<qname<<": truncated bit set, over TCP?"<<endl;
 
726
          return RCode::ServFail;
 
727
        }
 
728
        
 
729
        if(d_lwr.d_rcode==RCode::ServFail) {
 
730
          LOG<<prefix<<qname<<": "<<*tns<<" returned a ServFail, trying sibling IP or NS"<<endl;
 
731
          s_throttle.throttle(d_now.tv_sec,make_tuple(*remoteIP, qname, qtype.getCode()),60,3);
 
732
          continue;
 
733
        }
 
734
        LOG<<prefix<<qname<<": Got "<<(unsigned int)result.size()<<" answers from "<<*tns<<" ("<< remoteIP->toString() <<"), rcode="<<d_lwr.d_rcode<<", in "<<d_lwr.d_usec/1000<<"ms"<<endl;
 
735
 
 
736
        /*  // for you IPv6 fanatics :-)
 
737
        if(remoteIP->sin4.sin_family==AF_INET6)
 
738
          d_lwr.d_usec/=3;
 
739
        */
 
740
 
 
741
        s_nsSpeeds[*tns].submit(*remoteIP, d_lwr.d_usec, &d_now);
 
742
      }
 
743
 
 
744
      typedef map<pair<string, QType>, set<DNSResourceRecord>, TCacheComp > tcache_t;
 
745
      tcache_t tcache;
 
746
 
454
747
      // reap all answers from this packet that are acceptable
455
748
      for(LWRes::res_t::const_iterator i=result.begin();i!=result.end();++i) {
456
 
        if(i->qtype.getCode() < 1024) {
457
 
          LOG<<prefix<<qname<<": accept answer '"<<i->qname<<"|"<<i->qtype.getName()<<"|"<<i->content<<"' from '"<<auth<<"' nameservers? ";
458
 
        }
459
 
        else {
460
 
          LOG<<prefix<<qname<<": accept opaque answer '"<<i->qname<<"|"<<QType(i->qtype.getCode()-1024).getName()<<" from '"<<auth<<"' nameservers? ";
461
 
        }
462
 
        
463
 
        if(endsOn(i->qname, auth)) {
 
749
        LOG<<prefix<<qname<<": accept answer '"<<i->qname<<"|"<<i->qtype.getName()<<"|"<<i->content<<"' from '"<<auth<<"' nameservers? ";
 
750
        if(i->qtype.getCode()==QType::ANY) {
 
751
          LOG<<"NO! - we don't accept 'ANY' data"<<endl;
 
752
          continue;
 
753
        }
 
754
          
 
755
        if(dottedEndsOn(i->qname, auth)) {
464
756
          if(d_lwr.d_aabit && d_lwr.d_rcode==RCode::NoError && i->d_place==DNSResourceRecord::ANSWER && ::arg().contains("delegation-only",auth)) {
465
757
            LOG<<"NO! Is from delegation-only zone"<<endl;
466
758
            s_nodelegated++;
471
763
            
472
764
            DNSResourceRecord rr=*i;
473
765
            rr.d_place=DNSResourceRecord::ANSWER;
474
 
            rr.ttl+=d_now.tv_sec;
475
 
            //    rr.ttl=time(0)+10+10*rr.qtype.getCode();
476
 
            tcache[toLower(i->qname)+"|"+i->qtype.getName()].insert(rr);
 
766
 
 
767
            rr.ttl=min(86400*14U, rr.ttl); // limit TTL to two weeks
 
768
            rr.ttl += d_now.tv_sec;
 
769
 
 
770
            if(rr.qtype.getCode() == QType::NS) // people fiddle with the case
 
771
              rr.content=toLower(rr.content); // this must stay! (the cache can't be case-insensitive on the RHS of records)
 
772
            tcache[make_pair(i->qname,i->qtype)].insert(rr);
477
773
          }
478
774
        }         
479
775
        else
481
777
      }
482
778
    
483
779
      // supplant
484
 
      for(map<string,set<DNSResourceRecord> >::const_iterator i=tcache.begin();i!=tcache.end();++i) {
485
 
        vector<string>parts;
486
 
        stringtok(parts,i->first,"|");
487
 
        QType qt;
488
 
        if(parts.size()==2) {
489
 
          qt=parts[1];
490
 
          RC.replace(parts[0],qt,i->second);
491
 
        }
492
 
        else {
493
 
          qt=parts[0];
494
 
          RC.replace("",qt,i->second);
495
 
        }
 
780
      for(tcache_t::iterator i=tcache.begin();i!=tcache.end();++i) {
 
781
        if(i->second.size() > 1) {  // need to group the ttl to be the minimum of the RRSET (RFC 2181, 5.2)
 
782
          uint32_t lowestTTL=numeric_limits<uint32_t>::max();
 
783
          for(tcache_t::value_type::second_type::iterator j=i->second.begin(); j != i->second.end(); ++j)
 
784
            lowestTTL=min(lowestTTL, j->ttl);
 
785
          
 
786
          for(tcache_t::value_type::second_type::iterator j=i->second.begin(); j != i->second.end(); ++j)
 
787
            ((tcache_t::value_type::second_type::value_type*)&(*j))->ttl=lowestTTL;
 
788
        }
 
789
 
 
790
        RC.replace(d_now.tv_sec, i->first.first, i->first.second, i->second, d_lwr.d_aabit);
496
791
      }
497
 
      set<string> nsset;  
 
792
      set<string, CIStringCompare> nsset;  
498
793
      LOG<<prefix<<qname<<": determining status after receiving this packet"<<endl;
499
794
 
500
795
      bool done=false, realreferral=false, negindic=false;
501
796
      string newauth, soaname, newtarget;
502
797
 
503
798
      for(LWRes::res_t::const_iterator i=result.begin();i!=result.end();++i) {
504
 
        if(i->d_place==DNSResourceRecord::AUTHORITY && endsOn(qname,i->qname) && i->qtype.getCode()==QType::SOA && 
 
799
        if(i->d_place==DNSResourceRecord::AUTHORITY && dottedEndsOn(qname,i->qname) && i->qtype.getCode()==QType::SOA && 
505
800
           d_lwr.d_rcode==RCode::NXDomain) {
506
 
          LOG<<prefix<<qname<<": got negative caching indication for RECORD '"<<toLower(qname)+"'"<<endl;
 
801
          LOG<<prefix<<qname<<": got negative caching indication for RECORD '"<<qname+"'"<<endl;
507
802
          ret.push_back(*i);
508
803
 
509
804
          NegCacheEntry ne;
510
 
          ne.name=i->qname;
511
 
          ne.ttd=d_now.tv_sec + i->ttl;
512
 
          s_negcache[toLower(qname)]=ne;
 
805
 
 
806
          ne.d_qname=i->qname;
 
807
          ne.d_ttd=d_now.tv_sec + min(i->ttl, s_maxnegttl); // controversial
 
808
          ne.d_name=qname;
 
809
          ne.d_qtype=QType(0); // this encodes 'whole record'
 
810
          
 
811
          replacing_insert(s_negcache, ne);
513
812
          negindic=true;
514
813
        }
515
814
        else if(i->d_place==DNSResourceRecord::ANSWER && i->qname==qname && i->qtype.getCode()==QType::CNAME && (!(qtype==QType(QType::CNAME)))) {
516
815
          ret.push_back(*i);
517
 
          newtarget=toLowerCanonic(i->content);
 
816
          newtarget=i->content;
518
817
        }
519
818
        // for ANY answers we *must* have an authoritive answer
520
 
        else if(i->d_place==DNSResourceRecord::ANSWER && toLower(i->qname)==toLower(qname) && 
521
 
                (((i->qtype==qtype) || (i->qtype.getCode()>1024 && i->qtype.getCode()-1024==qtype.getCode())) || ( qtype==QType(QType::ANY) && 
522
 
                                                                                                                   d_lwr.d_aabit)))  {
523
 
          if(i->qtype.getCode() < 1024) {
524
 
            LOG<<prefix<<qname<<": answer is in: resolved to '"<< i->content<<"|"<<i->qtype.getName()<<"'"<<endl;
525
 
          }
526
 
          else {
527
 
            LOG<<prefix<<qname<<": answer is in: resolved to opaque record of type '"<<QType(i->qtype.getCode()-1024).getName()<<"'"<<endl;
528
 
          }
 
819
        else if(i->d_place==DNSResourceRecord::ANSWER && !Utility::strcasecmp(i->qname.c_str(),qname.c_str()) && 
 
820
                ( (i->qtype==qtype) ||
 
821
                                      ( qtype==QType(QType::ANY) && d_lwr.d_aabit)))  {
 
822
          
 
823
          LOG<<prefix<<qname<<": answer is in: resolved to '"<< i->content<<"|"<<i->qtype.getName()<<"'"<<endl;
529
824
 
530
825
          done=true;
531
826
          ret.push_back(*i);
532
827
        }
533
 
        else if(i->d_place==DNSResourceRecord::AUTHORITY && endsOn(qname,i->qname) && i->qtype.getCode()==QType::NS) { 
 
828
        else if(i->d_place==DNSResourceRecord::AUTHORITY && dottedEndsOn(qname,i->qname) && i->qtype.getCode()==QType::NS) { 
534
829
          if(moreSpecificThan(i->qname,auth)) {
535
830
            newauth=i->qname;
536
831
            LOG<<prefix<<qname<<": got NS record '"<<i->qname<<"' -> '"<<i->content<<"'"<<endl;
538
833
          }
539
834
          else 
540
835
            LOG<<prefix<<qname<<": got upwards/level NS record '"<<i->qname<<"' -> '"<<i->content<<"', had '"<<auth<<"'"<<endl;
541
 
          nsset.insert(toLowerCanonic(i->content));
 
836
          nsset.insert(i->content);
542
837
        }
543
 
        else if(i->d_place==DNSResourceRecord::AUTHORITY && endsOn(qname,i->qname) && i->qtype.getCode()==QType::SOA && 
 
838
        else if(!done && i->d_place==DNSResourceRecord::AUTHORITY && dottedEndsOn(qname,i->qname) && i->qtype.getCode()==QType::SOA && 
544
839
           d_lwr.d_rcode==RCode::NoError) {
545
 
          LOG<<prefix<<qname<<": got negative caching indication for '"<<toLower(qname)+"|"+i->qtype.getName()+"'"<<endl;
 
840
          LOG<<prefix<<qname<<": got negative caching indication for '"<< (qname+"|"+i->qtype.getName()+"'") <<endl;
546
841
          ret.push_back(*i);
547
842
          
548
843
          NegCacheEntry ne;
549
 
          ne.name=i->qname;
550
 
          ne.ttd=d_now.tv_sec + i->ttl;
551
 
          s_negcache[toLower(qname)+"|"+qtype.getName()]=ne;
 
844
          ne.d_qname=i->qname;
 
845
          ne.d_ttd=d_now.tv_sec + min(s_maxnegttl, i->ttl);
 
846
          ne.d_name=qname;
 
847
          ne.d_qtype=qtype;
 
848
          if(qtype.getCode()) {  // prevents us from blacking out a whole domain
 
849
            replacing_insert(s_negcache, ne);
 
850
          }
552
851
          negindic=true;
553
852
        }
554
853
      }
562
861
        return RCode::NXDomain;
563
862
      }
564
863
      if(!newtarget.empty()) {
 
864
        if(iequals(newtarget,qname)) {
 
865
          LOG<<prefix<<qname<<": status=got a CNAME referral to self, returning SERVFAIL"<<endl;
 
866
          return RCode::ServFail;
 
867
        }
 
868
        if(depth > 10) {
 
869
          LOG<<prefix<<qname<<": status=got a CNAME referral, but recursing too deep, returning SERVFAIL"<<endl;
 
870
          return RCode::ServFail;
 
871
        }
565
872
        LOG<<prefix<<qname<<": status=got a CNAME referral, starting over with "<<newtarget<<endl;
 
873
 
566
874
        set<GetBestNSAnswer>beenthere2;
567
 
        return doResolve(newtarget, qtype, ret,0,beenthere2);
 
875
        return doResolve(newtarget, qtype, ret, depth + 1, beenthere2);
568
876
      }
569
877
      if(nsset.empty() && !d_lwr.d_rcode) {
570
878
        LOG<<prefix<<qname<<": status=noerror, other types may exist, but we are done "<<(negindic ? "(have negative SOA)" : "")<<endl;
571
879
        return 0;
572
880
      }
573
881
      else if(realreferral) {
574
 
        LOG<<prefix<<qname<<": status=did not resolve, got "<<nsset.size()<<" NS, looping to them"<<endl;
 
882
        LOG<<prefix<<qname<<": status=did not resolve, got "<<(unsigned int)nsset.size()<<" NS, looping to them"<<endl;
575
883
        auth=newauth;
576
884
        nameservers=nsset;
577
885
        break; 
578
886
      }
579
 
      else {
580
 
        LOG<<prefix<<qname<<": status=NS "<<*tns<<" is lame for '"<<auth<<"', trying sibling NS"<<endl;
581
 
        s_throttle.throttle(d_now.tv_sec, *remoteIP+"|"+qname+"|"+qtype.getName(),60,0);
 
887
      else if(isCanonical(*tns)) {
 
888
        goto wasLame;;
582
889
      }
583
890
    }
584
891
  }
585
892
  return -1;
586
893
}
587
894
 
 
895
static bool uniqueComp(const DNSResourceRecord& a, const DNSResourceRecord& b)
 
896
{
 
897
  return(a.qtype==b.qtype && a.qname==b.qname && a.content==b.content);
 
898
}
 
899
 
588
900
void SyncRes::addCruft(const string &qname, vector<DNSResourceRecord>& ret)
589
901
{
590
902
  for(vector<DNSResourceRecord>::const_iterator k=ret.begin();k!=ret.end();++k)  // don't add stuff to an NXDOMAIN!
597
909
 
598
910
  LOG<<d_prefix<<qname<<": Starting additional processing"<<endl;
599
911
  vector<DNSResourceRecord> addit;
600
 
  bool doIPv6AP=::arg().mustDo("aaaa-additional-processing");
 
912
  static optional<bool> l_doIPv6AP;
 
913
  if(!l_doIPv6AP)
 
914
    l_doIPv6AP=::arg().mustDo("aaaa-additional-processing");
 
915
 
601
916
  for(vector<DNSResourceRecord>::const_iterator k=ret.begin();k!=ret.end();++k) 
602
 
    if((k->d_place==DNSResourceRecord::ANSWER && k->qtype==QType(QType::MX)) || 
 
917
    if( (k->d_place==DNSResourceRecord::ANSWER && (k->qtype==QType(QType::MX) || k->qtype==QType(QType::SRV)))  || 
603
918
       ((k->d_place==DNSResourceRecord::AUTHORITY || k->d_place==DNSResourceRecord::ANSWER) && k->qtype==QType(QType::NS))) {
604
919
      LOG<<d_prefix<<qname<<": record '"<<k->content<<"|"<<k->qtype.getName()<<"' needs IP for additional processing"<<endl;
605
 
      set<GetBestNSAnswer>beenthere;
606
 
      if(k->qtype==QType(QType::MX)) {
607
 
        string::size_type pos=k->content.find_first_not_of(" \t0123456789"); // chop off the priority
608
 
        if(pos!=string::npos) {
609
 
          doResolve(toLowerCanonic(k->content.substr(pos)), QType(QType::A),addit,1,beenthere);
610
 
          if(doIPv6AP)
611
 
            doResolve(toLowerCanonic(k->content.substr(pos)), QType(QType::AAAA),addit,1,beenthere);
612
 
        }
613
 
        else {
614
 
          doResolve(toLowerCanonic(k->content), QType(QType::A),addit,1,beenthere);
615
 
          if(doIPv6AP)
616
 
            doResolve(toLowerCanonic(k->content.substr(pos)), QType(QType::AAAA),addit,1,beenthere);
617
 
        }
618
 
      }
619
 
      else {
620
 
        doResolve(k->content,QType(QType::A),addit,1,beenthere);
621
 
        if(doIPv6AP)
622
 
          doResolve(k->content,QType(QType::AAAA),addit,1,beenthere);
623
 
      }
 
920
      set<GetBestNSAnswer> beenthere;
 
921
      vector<pair<string::size_type, string::size_type> > fields;
 
922
      vstringtok(fields, k->content, " ");
 
923
      string host;
 
924
      if(k->qtype==QType(QType::MX) && fields.size()==2)
 
925
        host=string(k->content.c_str() + fields[1].first, fields[1].second - fields[1].first);
 
926
      else if(k->qtype==QType(QType::NS))
 
927
        host=k->content;
 
928
      else if(k->qtype==QType(QType::SRV) && fields.size()==4)
 
929
        host=string(k->content.c_str() + fields[3].first, fields[3].second - fields[3].first);
 
930
      else 
 
931
        continue;
 
932
      doResolve(host, QType(QType::A), addit, 1, beenthere);
 
933
      if(*l_doIPv6AP)
 
934
        doResolve(host, QType(QType::AAAA), addit, 1, beenthere);
624
935
    }
625
936
  
 
937
  sort(addit.begin(), addit.end());
 
938
  addit.erase(unique(addit.begin(), addit.end(), uniqueComp), addit.end());
626
939
  for(vector<DNSResourceRecord>::iterator k=addit.begin();k!=addit.end();++k) {
627
940
    if(k->qtype.getCode()==QType::A || k->qtype.getCode()==QType::AAAA) {
628
941
      k->d_place=DNSResourceRecord::ADDITIONAL;
635
948
void SyncRes::addAuthorityRecords(const string& qname, vector<DNSResourceRecord>& ret, int depth)
636
949
{
637
950
  set<DNSResourceRecord> bestns;
638
 
  set<GetBestNSAnswer>beenthere;
639
 
  getBestNSFromCache(qname, bestns, depth,beenthere);
 
951
  set<GetBestNSAnswer> beenthere;
 
952
  bool dontcare;
 
953
  getBestNSFromCache(qname, bestns, &dontcare, depth, beenthere);
640
954
 
641
955
  for(set<DNSResourceRecord>::const_iterator k=bestns.begin();k!=bestns.end();++k) {
642
956
    DNSResourceRecord ns=*k;