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

« back to all changes in this revision

Viewing changes to pdns/backends/bind/bindbackend.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
 
/*
2
 
    PowerDNS Versatile Database Driven Nameserver
3
 
    Copyright (C) 2002  PowerDNS.COM BV
4
 
 
5
 
    This program is free software; you can redistribute it and/or modify
6
 
    it under the terms of the GNU General Public License version 2 as 
7
 
    published by the Free Software Foundation; 
8
 
 
9
 
    This program is distributed in the hope that it will be useful,
10
 
    but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 
    GNU General Public License for more details.
13
 
 
14
 
    You should have received a copy of the GNU General Public License
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
17
 
*/
18
 
#include <errno.h>
19
 
#include <string>
20
 
#include <map>
21
 
#include <set>
22
 
#include <sys/types.h>
23
 
#include <sys/stat.h>
24
 
#include <unistd.h>
25
 
 
26
 
using namespace std;
27
 
 
28
 
#include "dns.hh"
29
 
#include "dnsbackend.hh"
30
 
#include "bindbackend.hh"
31
 
#include "dnspacket.hh"
32
 
 
33
 
#include "zoneparser.hh"
34
 
#include "bindparser.hh"
35
 
#include "logger.hh"
36
 
#include "arguments.hh"
37
 
#include "huffman.hh"
38
 
#include "qtype.hh"
39
 
#include "misc.hh"
40
 
#include "dynlistener.hh"
41
 
#include "lock.hh"
42
 
using namespace std;
43
 
 
44
 
 
45
 
cmap_t BindBackend::d_qnames;
46
 
map<int,vector<vector<BBResourceRecord>* > > BindBackend::d_zone_id_map;  
47
 
set<string> BindBackend::s_contents;
48
 
HuffmanCodec BindBackend::s_hc;
49
 
 
50
 
map<unsigned int,BBDomainInfo> BindBackend::d_bbds;
51
 
 
52
 
int BindBackend::s_first=1;
53
 
pthread_mutex_t BindBackend::s_startup_lock=PTHREAD_MUTEX_INITIALIZER;
54
 
 
55
 
BBDomainInfo::BBDomainInfo()
56
 
{
57
 
  d_loaded=false;
58
 
  d_last_check=0;
59
 
  d_checknow=false;
60
 
  d_rwlock=new pthread_rwlock_t;
61
 
  d_status="Seen in bind configuration";
62
 
  d_confcount=0;
63
 
  //cout<<"Generated a new bbdomaininfo: "<<(void*)d_rwlock<<"/"<<getpid()<<endl;
64
 
  pthread_rwlock_init(d_rwlock,0);
65
 
}
66
 
 
67
 
void BBDomainInfo::setCheckInterval(time_t seconds)
68
 
{
69
 
  d_checkinterval=seconds;
70
 
}
71
 
 
72
 
bool BBDomainInfo::current()
73
 
{
74
 
  if(d_checknow)
75
 
    return false;
76
 
 
77
 
  if(!d_checknow && !d_checkinterval || (time(0)-d_lastcheck<d_checkinterval) || d_filename.empty())
78
 
    return true;
79
 
 
80
 
  return (getCtime()==d_ctime);
81
 
}
82
 
 
83
 
time_t BBDomainInfo::getCtime()
84
 
{
85
 
  struct stat buf;
86
 
  
87
 
  if(d_filename.empty() || stat(d_filename.c_str(),&buf)<0)
88
 
    return 0; 
89
 
  d_lastcheck=time(0);
90
 
  return buf.st_ctime;
91
 
}
92
 
 
93
 
void BBDomainInfo::setCtime()
94
 
{
95
 
  struct stat buf;
96
 
  if(stat(d_filename.c_str(),&buf)<0)
97
 
    return; 
98
 
  d_ctime=buf.st_ctime;
99
 
}
100
 
 
101
 
void BindBackend::setNotified(uint32_t id, uint32_t serial)
102
 
{
103
 
  d_bbds[id].d_lastnotified=serial;
104
 
}
105
 
 
106
 
void BindBackend::setFresh(uint32_t domain_id)
107
 
{
108
 
  d_bbds[domain_id].d_last_check=time(0);
109
 
}
110
 
 
111
 
bool BindBackend::startTransaction(const string &qname, int id)
112
 
{
113
 
  BBDomainInfo &bbd=d_bbds[d_transaction_id=id];
114
 
  d_transaction_tmpname=bbd.d_filename+"."+itoa(random());
115
 
  d_of=new ofstream(d_transaction_tmpname.c_str());
116
 
  if(!*d_of) {
117
 
    throw DBException("Unable to open temporary zonefile '"+d_transaction_tmpname+"': "+stringerror());
118
 
    unlink(d_transaction_tmpname.c_str());
119
 
    delete d_of;
120
 
    d_of=0;
121
 
  }
122
 
  
123
 
  *d_of<<"; Written by PowerDNS, don't edit!"<<endl;
124
 
  *d_of<<"; Zone '"+bbd.d_name+"' retrieved from master "<<bbd.d_master<<endl<<"; at "<<nowTime()<<endl;
125
 
  bbd.lock();
126
 
  return true;
127
 
}
128
 
 
129
 
bool BindBackend::commitTransaction()
130
 
{
131
 
  delete d_of;
132
 
  d_of=0;
133
 
  if(rename(d_transaction_tmpname.c_str(),d_bbds[d_transaction_id].d_filename.c_str())<0)
134
 
    throw DBException("Unable to commit (rename to: '"+d_bbds[d_transaction_id].d_filename+"') AXFRed zone: "+stringerror());
135
 
 
136
 
  queueReload(&d_bbds[d_transaction_id]);
137
 
  d_bbds[d_transaction_id].unlock();
138
 
  d_transaction_id=0;
139
 
  return true;
140
 
}
141
 
 
142
 
bool BindBackend::abortTransaction()
143
 
{
144
 
  if(d_transaction_id) {
145
 
    d_bbds[d_transaction_id].unlock();
146
 
    delete d_of;
147
 
    d_of=0;
148
 
    unlink(d_transaction_tmpname.c_str());
149
 
    d_transaction_id=0;
150
 
  }
151
 
  return true;
152
 
}
153
 
 
154
 
 
155
 
 
156
 
bool BindBackend::feedRecord(const DNSResourceRecord &r)
157
 
{
158
 
  string qname=r.qname;
159
 
  string domain=d_bbds[d_transaction_id].d_name;
160
 
 
161
 
  if(!stripDomainSuffix(&qname,domain)) 
162
 
    throw DBException("out-of-zone data '"+qname+"' during AXFR of zone '"+domain+"'");
163
 
 
164
 
  string content=r.content;
165
 
 
166
 
  // SOA needs stripping too! XXX FIXME
167
 
  switch(r.qtype.getCode()) {
168
 
  case QType::TXT:
169
 
    *d_of<<qname<<"\t"<<r.ttl<<"\t"<<r.qtype.getName()<<"\t\""<<r.content<<"\""<<endl;
170
 
    break;
171
 
  case QType::MX:
172
 
    if(!stripDomainSuffix(&content,domain))
173
 
      content+=".";
174
 
    *d_of<<qname<<"\t"<<r.ttl<<"\t"<<r.qtype.getName()<<"\t"<<r.priority<<"\t"<<content<<endl;
175
 
    break;
176
 
  case QType::CNAME:
177
 
  case QType::NS:
178
 
    if(!stripDomainSuffix(&content,domain))
179
 
      content+=".";
180
 
    *d_of<<qname<<"\t"<<r.ttl<<"\t"<<r.qtype.getName()<<"\t"<<content<<endl;
181
 
    break;
182
 
  default:
183
 
    *d_of<<qname<<"\t"<<r.ttl<<"\t"<<r.qtype.getName()<<"\t"<<r.content<<endl;
184
 
    break;
185
 
  }
186
 
 
187
 
  return true;
188
 
}
189
 
 
190
 
void BindBackend::getUpdatedMasters(vector<DomainInfo> *changedDomains)
191
 
{
192
 
  SOAData soadata;
193
 
  for(map<uint32_t,BBDomainInfo>::iterator i=d_bbds.begin();i!=d_bbds.end();++i) {
194
 
    if(!i->second.d_master.empty())
195
 
      continue;
196
 
    soadata.serial=0;
197
 
    try {
198
 
      getSOA(i->second.d_name,soadata); // we might not *have* a SOA yet
199
 
    }
200
 
    catch(...){}
201
 
    DomainInfo di;
202
 
    di.id=i->first;
203
 
    di.serial=soadata.serial;
204
 
    di.zone=i->second.d_name;
205
 
    di.last_check=i->second.d_last_check;
206
 
    di.backend=this;
207
 
    di.kind=DomainInfo::Master;
208
 
    if(!i->second.d_lastnotified)            // don't do notification storm on startup 
209
 
      i->second.d_lastnotified=soadata.serial;
210
 
    else
211
 
      if(soadata.serial!=i->second.d_lastnotified)
212
 
        changedDomains->push_back(di);
213
 
    
214
 
  }
215
 
}
216
 
 
217
 
void BindBackend::getUnfreshSlaveInfos(vector<DomainInfo> *unfreshDomains)
218
 
{
219
 
  for(map<uint32_t,BBDomainInfo>::const_iterator i=d_bbds.begin();i!=d_bbds.end();++i) {
220
 
    if(i->second.d_master.empty())
221
 
      continue;
222
 
    DomainInfo sd;
223
 
    sd.id=i->first;
224
 
    sd.zone=i->second.d_name;
225
 
    sd.master=i->second.d_master;
226
 
    sd.last_check=i->second.d_last_check;
227
 
    sd.backend=this;
228
 
    sd.kind=DomainInfo::Slave;
229
 
    SOAData soadata;
230
 
    soadata.serial=0;
231
 
    soadata.refresh=0;
232
 
    soadata.serial=0;
233
 
    soadata.db=(DNSBackend *)-1; // not sure if this is useful, inhibits any caches that might be around
234
 
    try {
235
 
      getSOA(i->second.d_name,soadata); // we might not *have* a SOA yet
236
 
    }
237
 
    catch(...){}
238
 
    sd.serial=soadata.serial;
239
 
    if(sd.last_check+soadata.refresh<(unsigned int)time(0))
240
 
      unfreshDomains->push_back(sd);    
241
 
  }
242
 
}
243
 
 
244
 
bool BindBackend::getDomainInfo(const string &domain, DomainInfo &di)
245
 
{
246
 
  for(map<uint32_t,BBDomainInfo>::const_iterator i=d_bbds.begin();i!=d_bbds.end();++i) {
247
 
    if(i->second.d_name==domain) {
248
 
      di.id=i->first;
249
 
      di.zone=domain;
250
 
      di.master=i->second.d_master;
251
 
      di.last_check=i->second.d_last_check;
252
 
      di.backend=this;
253
 
      di.kind=i->second.d_master.empty() ? DomainInfo::Master : DomainInfo::Slave;
254
 
      di.serial=0;
255
 
      try {
256
 
        SOAData sd;
257
 
        sd.serial=0;
258
 
        
259
 
        getSOA(i->second.d_name,sd); // we might not *have* a SOA yet
260
 
        di.serial=sd.serial;
261
 
      }
262
 
      catch(...){}
263
 
 
264
 
      return true;
265
 
    }
266
 
  }
267
 
  return false;
268
 
}
269
 
 
270
 
 
271
 
static string canonic(string ret)
272
 
{
273
 
  string::iterator i;
274
 
 
275
 
  for(i=ret.begin();
276
 
      i!=ret.end();
277
 
      ++i)
278
 
    *i=*i; //tolower(*i);
279
 
 
280
 
 
281
 
  if(*(i-1)=='.')
282
 
    ret.resize(i-ret.begin()-1);
283
 
  return ret;
284
 
}
285
 
 
286
 
/** This function adds a record to a domain with a certain id. */
287
 
void BindBackend::insert(int id, const string &qnameu, const string &qtype, const string &content, int ttl=300, int prio=25)
288
 
{
289
 
  static int s_count;
290
 
  static unsigned int len;
291
 
  static unsigned int ulen;
292
 
  DLOG(  
293
 
  if(!((s_count++)%10000))
294
 
    cerr<<"\r"<<s_count-1<<", "<<s_contents.size()<<" different contents, "<<d_qnames.size()<<" different qnames, "<<len/1000000<<"MB, saved: "<<
295
 
      (ulen-len)/1000;
296
 
  );
297
 
  string compressed;
298
 
  s_hc.encode(toLower(canonic(qnameu)),compressed);
299
 
  //  string(compressed).swap(compressed);
300
 
  //  cout<<"saved: "<<qnameu.size()-compressed.size()<<endl;
301
 
 
302
 
  vector<BBResourceRecord>::const_iterator i;
303
 
 
304
 
  if(d_qnames[compressed].empty()) {  // NEW! NEW! NEW! in de top 40!
305
 
    d_zone_id_map[id].push_back(&d_qnames[compressed]); 
306
 
    i=d_qnames[compressed].end();
307
 
  }
308
 
  else
309
 
    for(i=d_qnames[compressed].begin();i!=d_qnames[compressed].end();++i)
310
 
      if(((i)->qtype==QType::chartocode(qtype.c_str())))
311
 
        if((*(i)->content==canonic(content)))
312
 
          break; 
313
 
  
314
 
  // never saw this specific name/type/content triple before
315
 
  if(i==d_qnames[compressed].end()) {
316
 
    BBResourceRecord v=resourceMaker(id,qtype,canonic(content),ttl,prio);
317
 
    v.qnameptr=&d_qnames.find(compressed)->first;
318
 
    len+=compressed.size();
319
 
    ulen+=qnameu.size();
320
 
    d_qnames[compressed].push_back(v);
321
 
    
322
 
    d_qnames[compressed].reserve(0);
323
 
    //    vector<BBResourceRecord>&tmp=d_qnames[compressed];
324
 
    // vector<BBResourceRecord>(tmp).swap(tmp);
325
 
  }
326
 
  else {
327
 
    s_count--;
328
 
  }
329
 
}
330
 
 
331
 
 
332
 
/** Helper function that creates a BBResourceRecord and does s_content housekeeping */
333
 
BBResourceRecord BindBackend::resourceMaker(int id, const string &qtype, const string &content, int ttl, int prio)
334
 
{
335
 
  BBResourceRecord make;
336
 
  
337
 
  make.domain_id=id;
338
 
 
339
 
  make.qtype=QType::chartocode(qtype.c_str());
340
 
  if(!make.qtype)
341
 
    throw AhuException("Unknown qtype '"+qtype+"'"); // never leaves the BindBackend
342
 
 
343
 
  set<string>::const_iterator i=s_contents.find(content);
344
 
  if(i==s_contents.end()) {
345
 
    s_contents.insert(content);
346
 
    i=s_contents.find(content);
347
 
  }
348
 
  make.content=&*i;
349
 
  make.ttl=ttl;
350
 
  make.priority=prio;
351
 
  return make;
352
 
}
353
 
 
354
 
static BindBackend *us;
355
 
 
356
 
void BindBackend::reload()
357
 
{
358
 
  for(map<uint32_t,BBDomainInfo>::iterator i=us->d_bbds.begin();i!=us->d_bbds.end();++i) 
359
 
    i->second.d_checknow=true;
360
 
}
361
 
 
362
 
string BindBackend::DLReloadNowHandler(const vector<string>&parts, Utility::pid_t ppid)
363
 
{
364
 
  ostringstream ret;
365
 
  bool doReload=false;
366
 
  for(map<uint32_t,BBDomainInfo>::iterator j=us->d_bbds.begin();j!=us->d_bbds.end();++j) {
367
 
    doReload=false;
368
 
    if(parts.size()==1)
369
 
      doReload=true;
370
 
    else 
371
 
      for(vector<string>::const_iterator i=parts.begin()+1;i<parts.end();++i)                 // O(N) badness XXX FIXME
372
 
        if(*i==j->second.d_name) {
373
 
          doReload=true;
374
 
          break;
375
 
        }
376
 
    
377
 
    if(doReload) {
378
 
      j->second.lock();
379
 
 
380
 
      us->queueReload(&j->second);
381
 
      j->second.unlock();
382
 
      ret<<j->second.d_name<< (j->second.d_loaded ? "": "[rejected]") <<"\t"<<j->second.d_status<<"\n";
383
 
    }
384
 
    doReload=false;
385
 
  }
386
 
        
387
 
  return ret.str();
388
 
}
389
 
 
390
 
 
391
 
string BindBackend::DLDomStatusHandler(const vector<string>&parts, Utility::pid_t ppid)
392
 
{
393
 
  string ret;
394
 
  bool doPrint=false;
395
 
  for(map<uint32_t,BBDomainInfo>::iterator j=us->d_bbds.begin();j!=us->d_bbds.end();++j) {
396
 
    ostringstream line;
397
 
    doPrint=false;
398
 
    if(parts.size()==1)
399
 
      doPrint=true;
400
 
    else 
401
 
      for(vector<string>::const_iterator i=parts.begin()+1;i<parts.end();++i)                 // O(N) badness XXX FIXME
402
 
        if(*i==j->second.d_name) {
403
 
          doPrint=true;
404
 
          break;
405
 
        }
406
 
    
407
 
    if(doPrint)
408
 
      line<<j->second.d_name<< (j->second.d_loaded ? "": "[rejected]") <<"\t"<<j->second.d_status<<"\n";
409
 
    doPrint=false;
410
 
    ret+=line.str();
411
 
  }
412
 
        
413
 
  return ret;
414
 
}
415
 
 
416
 
 
417
 
string BindBackend::DLListRejectsHandler(const vector<string>&parts, Utility::pid_t ppid)
418
 
{
419
 
  ostringstream ret;
420
 
  for(map<uint32_t,BBDomainInfo>::iterator j=us->d_bbds.begin();j!=us->d_bbds.end();++j) 
421
 
    if(!j->second.d_loaded)
422
 
      ret<<j->second.d_name<<"\t"<<j->second.d_status<<endl;
423
 
        
424
 
  return ret.str();
425
 
}
426
 
 
427
 
static void callback(unsigned int domain_id, const string &domain, const string &qtype, const string &content, int ttl, int prio)
428
 
{
429
 
  us->insert(domain_id,domain,qtype,content,ttl,prio);
430
 
}
431
 
 
432
 
BindBackend::BindBackend(const string &suffix)
433
 
{
434
 
  d_logprefix="[bind1"+suffix+"backend]";
435
 
  setArgPrefix("bind1"+suffix);
436
 
  Lock l(&s_startup_lock);
437
 
 
438
 
  d_transaction_id=0;
439
 
  if(!s_first) {
440
 
    return;
441
 
  }
442
 
   
443
 
  s_first=0;
444
 
  if(!mustDo("enable-huffman"))
445
 
    s_hc.passthrough(true);
446
 
  
447
 
  if(mustDo("example-zones")) {
448
 
    insert(0,"www.example.com","A","1.2.3.4");
449
 
    insert(0,"example.com","SOA","ns1.example.com hostmaster.example.com");
450
 
    insert(0,"example.com","NS","ns1.example.com",86400);
451
 
    insert(0,"example.com","NS","ns2.example.com",86400);
452
 
    insert(0,"example.com","MX","mail.example.com",3600,25);
453
 
    insert(0,"example.com","MX","mail1.example.com",3600,25);
454
 
    insert(0,"mail.example.com","A","4.3.2.1");
455
 
    insert(0,"mail1.example.com","A","5.4.3.2");
456
 
    insert(0,"ns1.example.com","A","4.3.2.1");
457
 
    insert(0,"ns2.example.com","A","5.4.3.2");
458
 
      
459
 
    for(int i=0;i<1000;i++)
460
 
      insert(0,"host-"+itoa(i)+".example.com","A","2.3.4.5");
461
 
 
462
 
    BBDomainInfo bbd;
463
 
    bbd.d_name="example.com";
464
 
    bbd.d_filename="";
465
 
    bbd.d_id=0;
466
 
    d_bbds[0]=bbd; 
467
 
    d_bbds[0].d_loaded=true;
468
 
    d_bbds[0].d_status="parsed into memory at "+nowTime();
469
 
  }
470
 
  
471
 
  loadConfig();
472
 
  
473
 
 
474
 
  extern DynListener *dl;
475
 
  us=this;
476
 
  dl->registerFunc("BIND1-RELOAD-NOW", &DLReloadNowHandler);
477
 
  dl->registerFunc("BIND1-DOMAIN-STATUS", &DLDomStatusHandler);
478
 
  dl->registerFunc("BIND1-LIST-REJECTS", &DLListRejectsHandler);
479
 
}
480
 
 
481
 
 
482
 
void BindBackend::rediscover(string *status)
483
 
{
484
 
  loadConfig(status);
485
 
}
486
 
 
487
 
void BindBackend::loadConfig(string* status)
488
 
{
489
 
  static int domain_id=1;
490
 
 
491
 
  if(!getArg("config").empty()) {
492
 
    BindParser BP;
493
 
    try {
494
 
      BP.parse(getArg("config"));
495
 
    }
496
 
    catch(AhuException &ae) {
497
 
      L<<Logger::Error<<"Error parsing bind configuration: "<<ae.reason<<endl;
498
 
      throw;
499
 
    }
500
 
    
501
 
    ZoneParser ZP;
502
 
      
503
 
    vector<BindDomainInfo> domains=BP.getDomains();
504
 
    
505
 
    us=this;
506
 
 
507
 
    ZP.setDirectory(BP.getDirectory());
508
 
    ZP.setCallback(&callback);  
509
 
    L<<Logger::Warning<<d_logprefix<<" Parsing "<<domains.size()<<" domain(s), will report when done"<<endl;
510
 
    
511
 
    int rejected=0;
512
 
    int newdomains=0;
513
 
 
514
 
    map<unsigned int, BBDomainInfo> nbbds;
515
 
 
516
 
    for(vector<BindDomainInfo>::const_iterator i=domains.begin();
517
 
        i!=domains.end();
518
 
        ++i)
519
 
      {
520
 
        BBDomainInfo bbd;
521
 
        if(i->type!="master" && i->type!="slave") {
522
 
          L<<Logger::Warning<<d_logprefix<<" Warning! Skipping '"<<i->type<<"' zone '"<<i->name<<"'"<<endl;
523
 
          continue;
524
 
        }
525
 
        map<unsigned int, BBDomainInfo>::const_iterator j=d_bbds.begin();
526
 
        for(;j!=d_bbds.end();++j)
527
 
          if(j->second.d_name==i->name) {
528
 
            bbd=j->second;
529
 
            break;
530
 
          }
531
 
        if(j==d_bbds.end()) { // entirely new
532
 
          bbd.d_id=domain_id++;
533
 
 
534
 
          bbd.setCheckInterval(getArgAsNum("check-interval"));
535
 
          bbd.d_lastnotified=0;
536
 
          bbd.d_loaded=false;
537
 
        }
538
 
 
539
 
        bbd.d_name=i->name;
540
 
        bbd.d_filename=i->filename;
541
 
        bbd.d_master=i->master;
542
 
        
543
 
        nbbds[bbd.d_id]=bbd; 
544
 
        if(!bbd.d_loaded) {
545
 
          L<<Logger::Info<<d_logprefix<<" parsing '"<<i->name<<"' from file '"<<i->filename<<"'"<<endl;
546
 
          
547
 
          try {
548
 
            ZP.parse(i->filename,i->name,bbd.d_id); // calls callback for us
549
 
            nbbds[bbd.d_id].setCtime();
550
 
            nbbds[bbd.d_id].d_loaded=true;          // does this perform locking for us?
551
 
            nbbds[bbd.d_id].d_status="parsed into memory at "+nowTime();
552
 
            
553
 
          }
554
 
          catch(AhuException &ae) {
555
 
            ostringstream msg;
556
 
            msg<<" error at "+nowTime()+" parsing '"<<i->name<<"' from file '"<<i->filename<<"': "<<ae.reason;
557
 
            if(status)
558
 
              *status+=msg.str();
559
 
            nbbds[bbd.d_id].d_status=msg.str();
560
 
            L<<Logger::Warning<<d_logprefix<<msg.str()<<endl;
561
 
            rejected++;
562
 
          }
563
 
        }
564
 
        
565
 
        vector<vector<BBResourceRecord> *>&tmp=d_zone_id_map[bbd.d_id];  // shrink trick
566
 
        vector<vector<BBResourceRecord> *>(tmp).swap(tmp);
567
 
      }
568
 
 
569
 
 
570
 
    int remdomains=0;
571
 
    set<string> oldnames, newnames;
572
 
    for(map<unsigned int, BBDomainInfo>::const_iterator j=d_bbds.begin();j!=d_bbds.end();++j) {
573
 
      oldnames.insert(j->second.d_name);
574
 
    }
575
 
    for(map<unsigned int, BBDomainInfo>::const_iterator j=nbbds.begin();j!=nbbds.end();++j) {
576
 
      newnames.insert(j->second.d_name);
577
 
    }
578
 
 
579
 
    vector<string> diff;
580
 
    set_difference(oldnames.begin(), oldnames.end(), newnames.begin(), newnames.end(), back_inserter(diff));
581
 
    remdomains=diff.size();
582
 
    for(vector<string>::const_iterator k=diff.begin();k!=diff.end();++k)
583
 
      L<<Logger::Error<<"Removed: "<<*k<<endl;
584
 
 
585
 
    for(map<unsigned int, BBDomainInfo>::iterator j=d_bbds.begin();j!=d_bbds.end();++j) { // O(N*M)
586
 
      for(vector<string>::const_iterator k=diff.begin();k!=diff.end();++k)
587
 
        if(j->second.d_name==*k) {
588
 
          L<<Logger::Error<<"Removing records from zone '"<<j->second.d_name<<"' from memory"<<endl;
589
 
          j->second.lock();
590
 
          j->second.d_loaded=false;
591
 
          nukeZoneRecords(&j->second);
592
 
          j->second.unlock(); 
593
 
          break;
594
 
        }
595
 
    }
596
 
 
597
 
    vector<string> diff2;
598
 
    set_difference(newnames.begin(), newnames.end(), oldnames.begin(), oldnames.end(), back_inserter(diff2));
599
 
    newdomains=diff2.size();
600
 
 
601
 
    d_bbds.swap(nbbds); // commit
602
 
    ostringstream msg;
603
 
    msg<<" Done parsing domains, "<<rejected<<" rejected, "<<newdomains<<" new, "<<remdomains<<" removed"; 
604
 
    if(status)
605
 
      *status=msg.str();
606
 
 
607
 
    L<<Logger::Error<<d_logprefix<<msg.str()<<endl;
608
 
    L<<Logger::Info<<d_logprefix<<" Number of hash buckets: "<<d_qnames.bucket_count()<<", number of entries: "<<d_qnames.size()<< endl;
609
 
  }
610
 
}
611
 
 
612
 
/** nuke all records from memory, keep bbd intact though. Must be called with bbd lock held already! */
613
 
void BindBackend::nukeZoneRecords(BBDomainInfo *bbd)
614
 
{
615
 
  bbd->d_loaded=0; // block further access
616
 
    
617
 
  // this emtpies all d_qnames vectors belonging to this domain. We find these vectors via d_zone_id_map
618
 
  for(vector<vector<BBResourceRecord> *>::iterator i=d_zone_id_map[bbd->d_id].begin();
619
 
      i!=d_zone_id_map[bbd->d_id].end();++i) {
620
 
    (*i)->clear();
621
 
  }
622
 
  
623
 
  // empty our d_zone_id_map of the references to the now empty vectors (which are not gone from d_qnames, btw)
624
 
  d_zone_id_map[bbd->d_id].clear();
625
 
}
626
 
 
627
 
/** Must be called with bbd locked already. Will not be unlocked on return, is your own problem.
628
 
    Does not throw errors or anything, may update d_status however */
629
 
 
630
 
 
631
 
void BindBackend::queueReload(BBDomainInfo *bbd)
632
 
{
633
 
  // we reload *now* for the time being
634
 
  //cout<<"unlock domain"<<endl;
635
 
  bbd->unlock();
636
 
  //cout<<"lock it again"<<endl;
637
 
 
638
 
  bbd->lock();
639
 
  try {
640
 
    nukeZoneRecords(bbd);
641
 
    
642
 
    ZoneParser ZP;
643
 
    us=this;
644
 
    ZP.setCallback(&callback);  
645
 
    ZP.parse(bbd->d_filename,bbd->d_name,bbd->d_id);
646
 
    bbd->setCtime();
647
 
    // and raise d_loaded again!
648
 
    bbd->d_loaded=1;
649
 
    bbd->d_checknow=0;
650
 
    bbd->d_status="parsed into memory at "+nowTime();
651
 
    L<<Logger::Warning<<"Zone '"<<bbd->d_name<<"' ("<<bbd->d_filename<<") reloaded"<<endl;
652
 
  }
653
 
  catch(AhuException &ae) {
654
 
    ostringstream msg;
655
 
    msg<<" error at "+nowTime()+" parsing '"<<bbd->d_name<<"' from file '"<<bbd->d_filename<<"': "<<ae.reason;
656
 
    bbd->d_status=msg.str();
657
 
  }
658
 
}
659
 
 
660
 
void BindBackend::lookup(const QType &qtype,const string &qname, DNSPacket *pkt_p, int zoneId )
661
 
{
662
 
  d_handle=new BindBackend::handle;
663
 
  DLOG(L<<"BindBackend constructing handle for search for "<<qtype.getName()<<" for "<<
664
 
       qname<<endl);
665
 
 
666
 
  d_handle->qname=qname;
667
 
  d_handle->parent=this;
668
 
  d_handle->qtype=qtype;
669
 
  string compressed;
670
 
  s_hc.encode(toLower(qname),compressed);
671
 
  d_handle->d_records=d_qnames[compressed];
672
 
  d_handle->d_bbd=0;
673
 
  if(!d_handle->d_records.empty()) {
674
 
    BBDomainInfo& bbd=d_bbds[d_handle->d_records.begin()->domain_id];
675
 
    if(!bbd.d_loaded) {
676
 
      delete d_handle;
677
 
      throw DBException("Zone temporarily not available (file missing, or master dead)"); // fuck
678
 
    }
679
 
 
680
 
    if(!bbd.tryRLock()) {
681
 
      L<<Logger::Warning<<"Can't get read lock on zone '"<<bbd.d_name<<"'"<<endl;
682
 
      delete d_handle;
683
 
      throw DBException("Temporarily unavailable due to a zone lock"); // fuck
684
 
    }
685
 
      
686
 
 
687
 
    if(!bbd.current()) {
688
 
      L<<Logger::Warning<<"Zone '"<<bbd.d_name<<"' ("<<bbd.d_filename<<") needs reloading"<<endl;
689
 
      queueReload(&bbd);
690
 
    }
691
 
    d_handle->d_bbd=&bbd;
692
 
  }
693
 
  else {
694
 
    DLOG(L<<"Query with no results"<<endl);
695
 
  }
696
 
  d_handle->d_iter=d_handle->d_records.begin();
697
 
  d_handle->d_list=false;
698
 
}
699
 
 
700
 
BindBackend::handle::handle()
701
 
{
702
 
  d_bbd=0;
703
 
  count=0;
704
 
}
705
 
 
706
 
bool  BindBackend::get(DNSResourceRecord &r)
707
 
{
708
 
  if(!d_handle->get(r)) {
709
 
    delete d_handle;
710
 
    d_handle=0;
711
 
    return false;
712
 
  }
713
 
  return true;
714
 
}
715
 
 
716
 
bool BindBackend::handle::get(DNSResourceRecord &r)
717
 
{
718
 
  if(d_list)
719
 
    return get_list(r);
720
 
  else
721
 
    return get_normal(r);
722
 
}
723
 
 
724
 
bool BindBackend::handle::get_normal(DNSResourceRecord &r)
725
 
{
726
 
  DLOG(L << "BindBackend get() was called for "<<qtype.getName() << " record  for "<<
727
 
       qname<<"- "<<d_records.size()<<" available!"<<endl);
728
 
  
729
 
 
730
 
  while(d_iter!=d_records.end() && !(qtype=="ANY" || (d_iter)->qtype==QType(qtype).getCode())) {
731
 
    DLOG(L<<"Skipped "<<qname<<"/"<<QType(d_iter->qtype).getName()<<": '"<<*d_iter->content<<"'"<<endl);
732
 
    d_iter++;
733
 
  }
734
 
  if(d_iter==d_records.end()) { // we've reached the end
735
 
    if(d_bbd) {
736
 
      d_bbd->unlock();
737
 
      d_bbd=0;
738
 
    }
739
 
    return false;
740
 
  }
741
 
 
742
 
  DLOG(L << "BindBackend get() returning a rr with a "<<QType(d_iter->qtype).getCode()<<endl);
743
 
 
744
 
  r.qname=qname; // fill this in
745
 
  
746
 
  r.content=*(d_iter)->content;
747
 
  r.domain_id=(d_iter)->domain_id;
748
 
  r.qtype=(d_iter)->qtype;
749
 
  r.ttl=(d_iter)->ttl;
750
 
  r.priority=(d_iter)->priority;
751
 
  d_iter++;
752
 
 
753
 
  return true;
754
 
}
755
 
 
756
 
bool BindBackend::list(const string &target, int id)
757
 
{
758
 
  if(!d_zone_id_map.count(id))
759
 
    return false;
760
 
 
761
 
  d_handle=new BindBackend::handle;
762
 
  DLOG(L<<"BindBackend constructing handle for list of "<<id<<endl);
763
 
 
764
 
  d_handle->d_qname_iter=d_zone_id_map[id].begin();
765
 
  d_handle->d_qname_end=d_zone_id_map[id].end();   // iter now points to a vector of pointers to vector<BBResourceRecords>
766
 
  d_handle->d_riter=(*(d_handle->d_qname_iter))->begin();
767
 
  d_handle->d_rend=(*(d_handle->d_qname_iter))->end();
768
 
  // rend?
769
 
  d_handle->parent=this;
770
 
  d_handle->id=id;
771
 
  d_handle->d_list=true;
772
 
  return true;
773
 
}
774
 
 
775
 
// naam -> naamnummer
776
 
// naamnummer -> vector<BBResourceRecords>, BBResourceRecord bevat ook een pointer naar 
777
 
 
778
 
bool BindBackend::handle::get_list(DNSResourceRecord &r)
779
 
{
780
 
  DLOG(L << "BindBackend get_list()"<<endl);
781
 
 
782
 
  while(d_riter==d_rend) {
783
 
    DLOG(L<<"Starting new record"<<endl);
784
 
    d_qname_iter++;
785
 
    if(d_qname_iter==d_qname_end) { // we've reached the end of recordsets for this id
786
 
      DLOG(L<<"Really stop!"<<endl);
787
 
      return false;
788
 
    }
789
 
    d_riter=(*(d_qname_iter))->begin();
790
 
    d_rend=(*(d_qname_iter))->end();
791
 
  }
792
 
  // d_riter points to a pointer to BBResourceRecord 
793
 
 
794
 
  //  r.qname=qname; // fill this in  HOW?!
795
 
 
796
 
  r.qname=parent->s_hc.decode(*d_riter->qnameptr);
797
 
  
798
 
  r.content=*(d_riter)->content;
799
 
  r.domain_id=(d_riter)->domain_id;
800
 
  r.qtype=(d_riter)->qtype;
801
 
  r.ttl=(d_riter)->ttl;
802
 
  r.priority=(d_riter)->priority;
803
 
  d_riter++;
804
 
  return true;
805
 
}
806
 
 
807
 
bool BindBackend::isMaster(const string &name, const string &ip)
808
 
{
809
 
  for(map<uint32_t,BBDomainInfo>::iterator j=us->d_bbds.begin();j!=us->d_bbds.end();++j) 
810
 
    if(j->second.d_name==name)
811
 
      return j->second.d_master==ip;
812
 
  return false;
813
 
}
814
 
 
815
 
class BindFactory : public BackendFactory
816
 
{
817
 
   public:
818
 
      BindFactory() : BackendFactory("bind1") {}
819
 
 
820
 
      void declareArguments(const string &suffix="")
821
 
      {
822
 
         declare(suffix,"config","Location of named.conf","");
823
 
         declare(suffix,"example-zones","Install example zones","no");
824
 
         declare(suffix,"enable-huffman","Enable huffman compression","no");
825
 
         declare(suffix,"check-interval","Interval for zonefile changes","0");
826
 
      }
827
 
 
828
 
      DNSBackend *make(const string &suffix="")
829
 
      {
830
 
         return new BindBackend(suffix);
831
 
      }
832
 
};
833
 
 
834
 
 
835
 
 
836
 
 
837
 
//! Magic class that is activated when the dynamic library is loaded
838
 
class BindLoader
839
 
{
840
 
public:
841
 
  BindLoader()
842
 
  {
843
 
    BackendMakers().report(new BindFactory);
844
 
    L<<Logger::Notice<<"[BindBackend] This is the bind backend version "VERSION" ("__DATE__", "__TIME__") reporting"<<endl;
845
 
  }
846
 
};
847
 
static BindLoader bindloader;