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

« back to all changes in this revision

Viewing changes to pdns/backends/bind/bindbackend2.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) 2002-2005  PowerDNS.COM BV
 
3
    Copyright (C) 2002-2007  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 
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
19
#include <errno.h>
24
24
#include <sys/stat.h>
25
25
#include <unistd.h>
26
26
#include <fstream>
 
27
#include <fcntl.h>
27
28
#include <sstream>
28
 
 
 
29
#include <boost/bind.hpp>
 
30
#include <boost/algorithm/string.hpp>
29
31
using namespace std;
30
32
 
31
33
#include "dns.hh"
32
34
#include "dnsbackend.hh"
33
35
#include "bindbackend2.hh"
34
36
#include "dnspacket.hh"
35
 
 
36
 
#include "zoneparser.hh"
 
37
#include "zoneparser-tng.hh"
37
38
#include "bindparser.hh"
38
39
#include "logger.hh"
39
40
#include "arguments.hh"
49
50
    on start of query, we find the best zone to answer from
50
51
*/
51
52
 
52
 
Bind2Backend::name_id_map_t Bind2Backend::s_name_id_map;
53
 
 
54
53
// this map contains BB2DomainInfo structs, each of which contains a *pointer* to domain data
55
 
Bind2Backend::id_zone_map_t Bind2Backend::s_id_zone_map, Bind2Backend::s_staging_zone_map;
56
 
 
57
 
/* the model is that everything works from the s_id_zone_map, which is left alone most of the time.
58
 
   
59
 
   Any updates which might fail, build a new zone map in s_staging_zone_map
60
 
   Then we swap atomically, and clear the copy.
61
 
 
62
 
   Zone id's are kept constant so s_name_id_map doesn't need to be updated, except on removal or addition.
 
54
shared_ptr<Bind2Backend::State> Bind2Backend::s_state;
 
55
 
 
56
/* the model is that all our state hides in s_state. This State instance consists of the id_zone_map, which contains all our zone information, indexed by id.
 
57
   Then there is the name_id_map, which allows us to map a query to a zone id.
 
58
 
 
59
   The s_state is never written to, and it is a reference counted shared_ptr. Any function which needs to access the state
 
60
   should do so by making a shared_ptr copy of it, and do all its work on that copy.
 
61
 
 
62
   When I said s_state is never written to, I lied. No elements are ever added to it, or removed from it.
 
63
   Its values however may be changed, but not the keys. 
 
64
 
 
65
   When it is necessary to change the State, a deep copy is made, which is changed. Afterwards, 
 
66
   the s_state pointer is made to point to the new State.
 
67
 
 
68
   Anybody who is currently accessing the original holds a reference counted handle (shared_ptr) to it, which means it will stay around
 
69
   To save memory, we hold the records as a shared_ptr as well.
 
70
 
 
71
   Changes made to s_state directly should take the s_state_lock, so as to prevent writing to a stale copy.
63
72
*/
64
73
 
65
74
int Bind2Backend::s_first=1;
66
75
 
67
76
pthread_mutex_t Bind2Backend::s_startup_lock=PTHREAD_MUTEX_INITIALIZER;
68
 
pthread_mutex_t Bind2Backend::s_zonemap_lock=PTHREAD_MUTEX_INITIALIZER;
69
 
 
 
77
pthread_mutex_t Bind2Backend::s_state_lock=PTHREAD_MUTEX_INITIALIZER;
 
78
string Bind2Backend::s_binddirectory;  
70
79
/* when a query comes in, we find the most appropriate zone and answer from that */
71
80
 
72
81
BB2DomainInfo::BB2DomainInfo()
73
82
{
74
83
  d_loaded=false;
75
 
  d_last_check=0;
 
84
  d_lastcheck=0;
76
85
  d_checknow=false;
77
86
  d_status="Unknown";
78
87
}
87
96
  if(d_checknow)
88
97
    return false;
89
98
 
90
 
  if(!d_checkinterval || (time(0) - d_lastcheck < d_checkinterval ) || d_filename.empty())
 
99
  if(!d_checkinterval) 
 
100
    return true;
 
101
 
 
102
  if(time(0) - d_lastcheck < d_checkinterval)
 
103
    return true;
 
104
  
 
105
  if(d_filename.empty())
91
106
    return true;
92
107
 
93
108
  return (getCtime()==d_ctime);
113
128
 
114
129
void Bind2Backend::setNotified(uint32_t id, uint32_t serial)
115
130
{
116
 
  s_id_zone_map[id].d_lastnotified=serial;
 
131
  Lock l(&s_state_lock);
 
132
  s_state->id_zone_map[id].d_lastnotified=serial;
117
133
}
118
134
 
119
135
void Bind2Backend::setFresh(uint32_t domain_id)
120
136
{
121
 
  s_id_zone_map[domain_id].d_last_check=time(0);
 
137
  Lock l(&s_state_lock);
 
138
  s_state->id_zone_map[domain_id].d_lastcheck=time(0);
122
139
}
123
140
 
124
141
bool Bind2Backend::startTransaction(const string &qname, int id)
125
142
{
126
 
  BB2DomainInfo &bbd=s_id_zone_map[d_transaction_id=id];
 
143
  shared_ptr<State> state = s_state; // is only read from
 
144
 
 
145
  const BB2DomainInfo &bbd=state->id_zone_map[d_transaction_id=id];
 
146
 
127
147
  d_transaction_tmpname=bbd.d_filename+"."+itoa(random());
128
148
  d_of=new ofstream(d_transaction_tmpname.c_str());
129
149
  if(!*d_of) {
134
154
  }
135
155
  
136
156
  *d_of<<"; Written by PowerDNS, don't edit!"<<endl;
137
 
  *d_of<<"; Zone '"+bbd.d_name+"' retrieved from master "<<bbd.d_master<<endl<<"; at "<<nowTime()<<endl;
 
157
  *d_of<<"; Zone '"+bbd.d_name+"' retrieved from master "<<endl<<"; at "<<nowTime()<<endl; // insert master info here again
138
158
 
139
159
  return true;
140
160
}
143
163
{
144
164
  delete d_of;
145
165
  d_of=0;
146
 
  if(rename(d_transaction_tmpname.c_str(),s_id_zone_map[d_transaction_id].d_filename.c_str())<0)
147
 
    throw DBException("Unable to commit (rename to: '"+s_id_zone_map[d_transaction_id].d_filename+"') AXFRed zone: "+stringerror());
148
 
 
149
 
 
150
 
  queueReload(&s_id_zone_map[d_transaction_id]);
 
166
  shared_ptr<State> state = s_state;
 
167
 
 
168
  // this might fail if s_state was cycled during the AXFR
 
169
  if(rename(d_transaction_tmpname.c_str(), state->id_zone_map[d_transaction_id].d_filename.c_str())<0)
 
170
    throw DBException("Unable to commit (rename to: '" + state->id_zone_map[d_transaction_id].d_filename+"') AXFRed zone: "+stringerror());
 
171
 
 
172
  queueReload(&state->id_zone_map[d_transaction_id]);
151
173
 
152
174
  d_transaction_id=0;
153
175
 
169
191
bool Bind2Backend::feedRecord(const DNSResourceRecord &r)
170
192
{
171
193
  string qname=r.qname;
172
 
  string domain=s_id_zone_map[d_transaction_id].d_name;
 
194
 
 
195
  const shared_ptr<State> state = s_state;
 
196
  string domain = state->id_zone_map[d_transaction_id].d_name;
173
197
 
174
198
  if(!stripDomainSuffix(&qname,domain)) 
175
199
    throw DBException("out-of-zone data '"+qname+"' during AXFR of zone '"+domain+"'");
178
202
 
179
203
  // SOA needs stripping too! XXX FIXME - also, this should not be here I think
180
204
  switch(r.qtype.getCode()) {
181
 
  case QType::TXT:
182
 
    *d_of<<qname<<"\t"<<r.ttl<<"\t"<<r.qtype.getName()<<"\t\""<<r.content<<"\""<<endl;
183
 
    break;
184
205
  case QType::MX:
185
 
    if(!stripDomainSuffix(&content,domain))
 
206
    if(!stripDomainSuffix(&content, domain))
186
207
      content+=".";
 
208
  case QType::SRV:
187
209
    *d_of<<qname<<"\t"<<r.ttl<<"\t"<<r.qtype.getName()<<"\t"<<r.priority<<"\t"<<content<<endl;
188
210
    break;
189
211
  case QType::CNAME:
190
212
  case QType::NS:
191
 
    if(!stripDomainSuffix(&content,domain))
 
213
    if(!stripDomainSuffix(&content, domain))
192
214
      content+=".";
193
215
    *d_of<<qname<<"\t"<<r.ttl<<"\t"<<r.qtype.getName()<<"\t"<<content<<endl;
194
216
    break;
203
225
void Bind2Backend::getUpdatedMasters(vector<DomainInfo> *changedDomains)
204
226
{
205
227
  SOAData soadata;
206
 
  for(id_zone_map_t::iterator i=s_id_zone_map.begin();i!=s_id_zone_map.end();++i) {
207
 
    if(!i->second.d_master.empty())
 
228
 
 
229
  //  Lock l(&s_state_lock); // we don't really change the zone map, just flip a bit
 
230
 
 
231
  for(id_zone_map_t::iterator i = s_state->id_zone_map.begin(); i != s_state->id_zone_map.end() ; ++i) {
 
232
    if(!i->second.d_masters.empty())
208
233
      continue;
209
234
    soadata.serial=0;
210
235
    try {
211
 
      getSOA(i->second.d_name,soadata); // we might not *have* a SOA yet
 
236
      this->getSOA(i->second.d_name, soadata); // we might not *have* a SOA yet, but this might trigger a load of it
212
237
    }
213
238
    catch(...){}
214
239
    DomainInfo di;
215
240
    di.id=i->first;
216
241
    di.serial=soadata.serial;
217
242
    di.zone=i->second.d_name;
218
 
    di.last_check=i->second.d_last_check;
 
243
    di.last_check=i->second.d_lastcheck;
219
244
    di.backend=this;
220
245
    di.kind=DomainInfo::Master;
221
246
    if(!i->second.d_lastnotified)            // don't do notification storm on startup 
228
253
 
229
254
void Bind2Backend::getUnfreshSlaveInfos(vector<DomainInfo> *unfreshDomains)
230
255
{
231
 
  for(id_zone_map_t::const_iterator i=s_id_zone_map.begin();i!=s_id_zone_map.end();++i) {
232
 
    if(i->second.d_master.empty())
 
256
  shared_ptr<State> state = s_state;
 
257
  for(id_zone_map_t::const_iterator i = state->id_zone_map.begin(); i != state->id_zone_map.end() ; ++i) {
 
258
    if(i->second.d_masters.empty())
233
259
      continue;
234
260
    DomainInfo sd;
235
261
    sd.id=i->first;
236
262
    sd.zone=i->second.d_name;
237
 
    sd.master=i->second.d_master;
238
 
    sd.last_check=i->second.d_last_check;
 
263
    sd.masters=i->second.d_masters;
 
264
    sd.last_check=i->second.d_lastcheck;
239
265
    sd.backend=this;
240
266
    sd.kind=DomainInfo::Slave;
241
267
    SOAData soadata;
255
281
 
256
282
bool Bind2Backend::getDomainInfo(const string &domain, DomainInfo &di)
257
283
{
258
 
  for(id_zone_map_t::const_iterator i=s_id_zone_map.begin();i!=s_id_zone_map.end();++i) {
 
284
  shared_ptr<State> state = s_state;
 
285
  for(id_zone_map_t::const_iterator i = state->id_zone_map.begin(); i != state->id_zone_map.end() ; ++i) {
259
286
    if(i->second.d_name==domain) {
260
287
      di.id=i->first;
261
288
      di.zone=domain;
262
 
      di.master=i->second.d_master;
263
 
      di.last_check=i->second.d_last_check;
 
289
      di.masters=i->second.d_masters;
 
290
      di.last_check=i->second.d_lastcheck;
264
291
      di.backend=this;
265
 
      di.kind=i->second.d_master.empty() ? DomainInfo::Master : DomainInfo::Slave;
 
292
      di.kind=i->second.d_masters.empty() ? DomainInfo::Master : DomainInfo::Slave;
266
293
      di.serial=0;
267
294
      try {
268
295
        SOAData sd;
296
323
  return ret;
297
324
}
298
325
 
299
 
static Bind2Backend *us;
300
 
 
301
 
static void InsertionCallback(unsigned int domain_id, const string &domain, const string &qtype, const string &content, int ttl, int prio)
302
 
{
303
 
  us->insert(domain_id, domain, qtype, content, ttl, prio);
304
 
}
305
 
 
306
326
set<string> contents;
307
327
 
308
 
/** This function adds a record to a domain with a certain id. 
 
328
/** THIS IS AN INTERNAL FUNCTION! It does moadnsparser prio impedence matching
 
329
    This function adds a record to a domain with a certain id. 
309
330
    Much of the complication is due to the efforts to benefit from std::string reference counting copy on write semantics */
310
 
void Bind2Backend::insert(int id, const string &qnameu, const string &qtype, const string &content, int ttl=300, int prio=25)
 
331
void Bind2Backend::insert(shared_ptr<State> stage, int id, const string &qnameu, const QType &qtype, const string &content, int ttl=300, int prio=25)
311
332
{
 
333
  BB2DomainInfo bb2 = stage->id_zone_map[id];
312
334
  Bind2DNSRecord bdr;
313
335
 
314
 
  BB2DomainInfo& bb2=s_staging_zone_map[id];
315
 
 
316
 
  vector<Bind2DNSRecord>& records=*bb2.d_records;
 
336
  vector<Bind2DNSRecord>& records=*bb2.d_records; 
317
337
 
318
338
  bdr.qname=toLower(canonic(qnameu));
319
339
  if(bdr.qname==toLower(bb2.d_name))
321
341
  else if(bdr.qname.length() > bb2.d_name.length())
322
342
    bdr.qname.resize(bdr.qname.length() - (bb2.d_name.length() + 1));
323
343
  else
324
 
    throw AhuException("Trying to insert non-zone data, name='"+bdr.qname+"', zone='"+s_staging_zone_map[id].d_name+"'");
 
344
    throw AhuException("Trying to insert non-zone data, name='"+bdr.qname+"', zone='" + s_state->id_zone_map[id].d_name+"'");
325
345
 
326
346
  bdr.qname.swap(bdr.qname);
327
347
 
328
348
  if(!records.empty() && bdr.qname==(records.end()-1)->qname)
329
349
    bdr.qname=(records.end()-1)->qname;
330
350
 
331
 
  bdr.qtype=QType(qtype.c_str()).getCode();
332
 
  bdr.content=canonic(content); // I think this is wrong, the zoneparser should not come up with . terminated stuff XXX FIXME
 
351
  bdr.qtype=qtype.getCode();
 
352
  bdr.content=content; 
 
353
 
 
354
  if(bdr.qtype == QType::MX || bdr.qtype == QType::SRV) { 
 
355
    prio=atoi(bdr.content.c_str());
 
356
    
 
357
    string::size_type pos = bdr.content.find_first_not_of("0123456789");
 
358
    if(pos != string::npos)
 
359
      erase_head(bdr.content, pos);
 
360
    trim_left(bdr.content);
 
361
  }
 
362
  
 
363
  if(bdr.qtype==QType::CNAME || bdr.qtype==QType::MX || bdr.qtype==QType::NS || bdr.qtype==QType::AFSDB)
 
364
    bdr.content=canonic(bdr.content); // I think this is wrong, the zoneparser should not come up with . terminated stuff XXX FIXME
 
365
 
333
366
  set<string>::const_iterator i=contents.find(bdr.content);
334
367
  if(i!=contents.end())
335
368
   bdr.content=*i;
339
372
 
340
373
  bdr.ttl=ttl;
341
374
  bdr.priority=prio;
342
 
 
 
375
  
343
376
  records.push_back(bdr);
344
377
}
345
378
 
346
379
void Bind2Backend::reload()
347
380
{
348
 
  for(id_zone_map_t::iterator i=us->s_id_zone_map.begin();i!=us->s_id_zone_map.end();++i) 
 
381
  Lock l(&s_state_lock);
 
382
  for(id_zone_map_t::iterator i = s_state->id_zone_map.begin(); i != s_state->id_zone_map.end(); ++i) 
349
383
    i->second.d_checknow=true;
350
384
}
351
385
 
352
386
string Bind2Backend::DLReloadNowHandler(const vector<string>&parts, Utility::pid_t ppid)
353
387
{
 
388
  shared_ptr<State> state = s_state;
354
389
  ostringstream ret;
355
390
 
356
391
  for(vector<string>::const_iterator i=parts.begin()+1;i<parts.end();++i) {
357
 
    if(s_name_id_map.count(*i)) {
358
 
      BB2DomainInfo& bbd=s_id_zone_map[s_name_id_map[*i]];
 
392
    if(state->name_id_map.count(*i)) {
 
393
      BB2DomainInfo& bbd=state->id_zone_map[state->name_id_map[*i]];
359
394
      
360
 
      us->queueReload(&bbd);
 
395
      queueReload(&bbd);
361
396
      ret<< *i << ": "<< (bbd.d_loaded ? "": "[rejected]") <<"\t"<<bbd.d_status<<"\n";      
362
397
    }
363
398
    else
372
407
string Bind2Backend::DLDomStatusHandler(const vector<string>&parts, Utility::pid_t ppid)
373
408
{
374
409
  ostringstream ret;
 
410
  shared_ptr<State> state = s_state;;
 
411
      
375
412
  if(parts.size() > 1) {
376
413
    for(vector<string>::const_iterator i=parts.begin()+1;i<parts.end();++i) {
377
 
      if(s_name_id_map.count(*i)) {
378
 
        BB2DomainInfo& bbd=s_id_zone_map[s_name_id_map[*i]];
 
414
      if(state->name_id_map.count(*i)) {
 
415
        BB2DomainInfo& bbd=state->id_zone_map[state->name_id_map[*i]];  // XXX s_name_id_map needs trick as well
379
416
        ret<< *i << ": "<< (bbd.d_loaded ? "": "[rejected]") <<"\t"<<bbd.d_status<<"\n";      
380
417
    }
381
418
      else
383
420
    }    
384
421
  }
385
422
  else
386
 
    for(id_zone_map_t::iterator i=us->s_id_zone_map.begin(); i!=us->s_id_zone_map.end(); ++i) 
 
423
    for(id_zone_map_t::iterator i=state->id_zone_map.begin(); i!=state->id_zone_map.end(); ++i) 
387
424
      ret<< i->second.d_name << ": "<< (i->second.d_loaded ? "": "[rejected]") <<"\t"<<i->second.d_status<<"\n";      
388
425
 
389
426
  if(ret.str().empty())
395
432
 
396
433
string Bind2Backend::DLListRejectsHandler(const vector<string>&parts, Utility::pid_t ppid)
397
434
{
 
435
  shared_ptr<State> state = s_state;
 
436
 
398
437
  ostringstream ret;
399
 
  for(id_zone_map_t::iterator j=us->s_id_zone_map.begin();j!=us->s_id_zone_map.end();++j) 
 
438
  for(id_zone_map_t::iterator j = state->id_zone_map.begin(); j != state->id_zone_map.end(); ++j) 
400
439
    if(!j->second.d_loaded)
401
440
      ret<<j->second.d_name<<"\t"<<j->second.d_status<<endl;
402
441
        
403
442
  return ret.str();
404
443
}
405
444
 
406
 
 
407
445
Bind2Backend::Bind2Backend(const string &suffix)
408
446
{
409
447
#if __GNUC__ >= 3
417
455
  if(!s_first) {
418
456
    return;
419
457
  }
420
 
   
421
458
  s_first=0;
422
 
  
 
459
  s_state = shared_ptr<State>(new State);
423
460
  loadConfig();
424
461
 
425
462
  extern DynListener *dl;
426
 
  us=this;
427
463
  dl->registerFunc("BIND-RELOAD-NOW", &DLReloadNowHandler);
428
464
  dl->registerFunc("BIND-DOMAIN-STATUS", &DLDomStatusHandler);
429
465
  dl->registerFunc("BIND-LIST-REJECTS", &DLListRejectsHandler);
430
466
}
431
467
 
 
468
Bind2Backend::~Bind2Backend()
 
469
{
 
470
 
 
471
}
 
472
 
432
473
void Bind2Backend::rediscover(string *status)
433
474
{
434
475
  loadConfig(status);
435
476
}
436
477
 
 
478
static void prefetchFile(const std::string& fname)
 
479
{
 
480
#if 0
 
481
  static int fd;
 
482
  if(fd > 0)
 
483
    close(fd);
 
484
  fd=open(fname.c_str(), O_RDONLY);
 
485
  if(fd < 0)
 
486
    return;
 
487
 
 
488
  posix_fadvise(fd, 0, 0, POSIX_FADV_WILLNEED);
 
489
#endif 
 
490
}
 
491
 
437
492
void Bind2Backend::loadConfig(string* status)
438
493
{
439
494
  // Interference with createSlaveDomain()
440
 
  Lock l(&s_zonemap_lock);
 
495
  Lock l(&s_state_lock);
441
496
  
442
497
  static int domain_id=1;
443
 
  s_staging_zone_map.clear();
 
498
 
 
499
  shared_ptr<State> staging = shared_ptr<State>(new State);
 
500
 
444
501
  if(!getArg("config").empty()) {
445
502
    BindParser BP;
446
503
    try {
450
507
      L<<Logger::Error<<"Error parsing bind configuration: "<<ae.reason<<endl;
451
508
      throw;
452
509
    }
453
 
 
454
 
    ZoneParser ZP;
455
510
      
456
511
    vector<BindDomainInfo> domains=BP.getDomains();
457
512
    
458
 
    us=this;
459
 
 
460
 
    d_binddirectory=BP.getDirectory();
461
 
    ZP.setDirectory(d_binddirectory);
462
 
    ZP.setCallback(&InsertionCallback);  
 
513
    s_binddirectory=BP.getDirectory();
 
514
    //    ZP.setDirectory(d_binddirectory);
463
515
 
464
516
    L<<Logger::Warning<<d_logprefix<<" Parsing "<<domains.size()<<" domain(s), will report when done"<<endl;
465
517
    
466
518
    int rejected=0;
467
519
    int newdomains=0;
468
520
 
 
521
    //    random_shuffle(domains.begin(), domains.end());
 
522
    struct stat st;
 
523
      
 
524
    for(vector<BindDomainInfo>::iterator i=domains.begin(); i!=domains.end(); ++i) 
 
525
    {
 
526
      if(stat(i->filename.c_str(), &st) == 0) {
 
527
        i->d_dev = st.st_dev;
 
528
        i->d_ino = st.st_ino;
 
529
      }
 
530
    }
 
531
 
 
532
    sort(domains.begin(), domains.end()); // put stuff in inode order
 
533
 
469
534
    for(vector<BindDomainInfo>::const_iterator i=domains.begin();
470
535
        i!=domains.end();
471
536
        ++i) 
477
542
 
478
543
        BB2DomainInfo* bbd=0;
479
544
 
480
 
        if(!s_name_id_map.count(i->name)) { // is it fully new?
481
 
          bbd=&s_staging_zone_map[domain_id];
 
545
        if(!s_state->name_id_map.count(i->name)) { // is it fully new?
 
546
          bbd=&staging->id_zone_map[domain_id];
482
547
          bbd->d_id=domain_id++;
483
 
          s_name_id_map[i->name]=bbd->d_id;
484
 
 
 
548
        
485
549
          // this isn't necessary, we do this on the actual load
486
550
          //      bbd->d_records=shared_ptr<vector<Bind2DNSRecord> > (new vector<Bind2DNSRecord>);
487
551
 
490
554
          bbd->d_loaded=false;
491
555
        }
492
556
        else {  // no, we knew about it already
493
 
          s_staging_zone_map[s_name_id_map[i->name]]=s_id_zone_map[s_name_id_map[i->name]];
494
 
          bbd=&s_staging_zone_map[s_name_id_map[i->name]];
 
557
          staging->id_zone_map[s_state->name_id_map[i->name]] = s_state->id_zone_map[s_state->name_id_map[i->name]]; // these should all be read-only on s_state
 
558
          bbd = &staging->id_zone_map[s_state->name_id_map[i->name]];
495
559
        }
496
560
        
 
561
        staging->name_id_map[i->name]=bbd->d_id; // fill out name -> id map
497
562
 
498
563
        // overwrite what we knew about the domain
499
564
        bbd->d_name=i->name;
500
565
        bbd->d_filename=i->filename;
501
 
        bbd->d_master=i->master;
 
566
        bbd->d_masters=i->masters;
502
567
        
503
568
        if(!bbd->d_loaded || !bbd->current()) {
504
 
          L<<Logger::Info<<d_logprefix<<" parsing '"<<i->name<<"' from file '"<<i->filename<<"'"<<endl;
 
569
          //      L<<Logger::Info<<d_logprefix<<" parsing '"<<i->name<<"' from file '"<<i->filename<<"'"<<endl;
505
570
          
506
571
          try {
507
 
            // we need to allocate a new vector so we don't kill the original
 
572
            // we need to allocate a new vector so we don't kill the original, which is still in use!
508
573
            bbd->d_records=shared_ptr<vector<Bind2DNSRecord> > (new vector<Bind2DNSRecord>); 
509
574
 
510
 
            ZP.parse(i->filename, i->name, bbd->d_id); // calls callback for us
511
 
            L<<Logger::Info<<d_logprefix<<" sorting '"<<i->name<<"'"<<endl;
512
 
 
513
 
            sort(s_staging_zone_map[bbd->d_id].d_records->begin(), s_staging_zone_map[bbd->d_id].d_records->end());
514
 
 
515
 
            s_staging_zone_map[bbd->d_id].setCtime();
516
 
            s_staging_zone_map[bbd->d_id].d_loaded=true; 
517
 
            s_staging_zone_map[bbd->d_id].d_status="parsed into memory at "+nowTime();
 
575
            ZoneParserTNG zpt(i->filename, i->name, BP.getDirectory());
 
576
            DNSResourceRecord rr;
 
577
            while(zpt.get(rr)) {
 
578
              insert(staging, bbd->d_id, rr.qname, rr.qtype, rr.content, rr.ttl, rr.priority);
 
579
            }
 
580
 
 
581
            //      ZP.parse(i->filename, i->name, bbd->d_id); // calls callback for us
 
582
            //      L<<Logger::Info<<d_logprefix<<" sorting '"<<i->name<<"'"<<endl;
 
583
 
 
584
            sort(staging->id_zone_map[bbd->d_id].d_records->begin(), staging->id_zone_map[bbd->d_id].d_records->end());
 
585
            
 
586
            staging->id_zone_map[bbd->d_id].setCtime();
 
587
            staging->id_zone_map[bbd->d_id].d_loaded=true; 
 
588
            staging->id_zone_map[bbd->d_id].d_status="parsed into memory at "+nowTime();
518
589
 
519
590
            contents.clear();
520
 
            //  s_staging_zone_map[bbd->d_id].d_records->swap(*s_staging_zone_map[bbd->d_id].d_records);
 
591
            //  s_stage->id_zone_map[bbd->d_id].d_records->swap(*s_staging_zone_map[bbd->d_id].d_records);
521
592
          }
522
593
          catch(AhuException &ae) {
523
594
            ostringstream msg;
525
596
 
526
597
            if(status)
527
598
              *status+=msg.str();
528
 
            s_staging_zone_map[bbd->d_id].d_status=msg.str();
 
599
            staging->id_zone_map[bbd->d_id].d_status=msg.str();
 
600
            L<<Logger::Warning<<d_logprefix<<msg.str()<<endl;
 
601
            rejected++;
 
602
          }
 
603
          catch(exception &ae) {
 
604
            ostringstream msg;
 
605
            msg<<" error at "+nowTime()+" parsing '"<<i->name<<"' from file '"<<i->filename<<"': "<<ae.what();
 
606
 
 
607
            if(status)
 
608
              *status+=msg.str();
 
609
            staging->id_zone_map[bbd->d_id].d_status=msg.str();
529
610
            L<<Logger::Warning<<d_logprefix<<msg.str()<<endl;
530
611
            rejected++;
531
612
          }
539
620
    // figure out which domains were new and which vanished
540
621
    int remdomains=0;
541
622
    set<string> oldnames, newnames;
542
 
    for(id_zone_map_t::const_iterator j=s_id_zone_map.begin();j!=s_id_zone_map.end();++j) {
 
623
    for(id_zone_map_t::const_iterator j=s_state->id_zone_map.begin();j != s_state->id_zone_map.end();++j) {
543
624
      oldnames.insert(j->second.d_name);
544
625
    }
545
 
    for(id_zone_map_t::const_iterator j=s_staging_zone_map.begin();j!=s_staging_zone_map.end();++j) {
 
626
    for(id_zone_map_t::const_iterator j=staging->id_zone_map.begin(); j!= staging->id_zone_map.end(); ++j) {
546
627
      newnames.insert(j->second.d_name);
547
628
    }
548
629
 
549
630
    vector<string> diff;
550
631
    set_difference(oldnames.begin(), oldnames.end(), newnames.begin(), newnames.end(), back_inserter(diff));
551
632
    remdomains=diff.size();
552
 
        
 
633
 
 
634
#if 0        
553
635
    // remove domains from the *name* map, delete their pointer
554
636
    for(vector<string>::const_iterator k=diff.begin();k!=diff.end(); ++k) {
555
637
      L<<Logger::Error<<"Removing domain: "<<*k<<endl;
556
 
      s_name_id_map.erase(*k);
 
638
      s_state->name_id_map.erase(*k);
557
639
    }
558
640
 
559
 
    // now remove from the s_id_zone_map
560
 
    for(id_zone_map_t::iterator j=s_id_zone_map.begin();j!=s_id_zone_map.end();++j) { // O(N*M)
 
641
    // now remove from the s_state->id_zone_map
 
642
    for(id_zone_map_t::iterator j=s_state->id_zone_map.begin();j!=s_state->id_zone_map.end();++j) { // O(N*M)
561
643
      for(vector<string>::const_iterator k=diff.begin();k!=diff.end();++k)
562
644
        if(j->second.d_name==*k) {
563
645
          L<<Logger::Error<<"Removing records from zone '"<<j->second.d_name<<"' from memory"<<endl;
568
650
          break;
569
651
        }
570
652
    }
 
653
#endif
571
654
 
572
655
    // count number of entirely new domains
573
656
    vector<string> diff2;
574
657
    set_difference(newnames.begin(), newnames.end(), oldnames.begin(), oldnames.end(), back_inserter(diff2));
575
658
    newdomains=diff2.size();
576
 
 
577
 
    s_id_zone_map.swap(s_staging_zone_map); // commit
578
 
    s_staging_zone_map.clear();             // and cleanup
 
659
    
 
660
    s_state.swap(staging); // and boy do we hope this is a threadsafe operation!
579
661
 
580
662
    // report
581
663
    ostringstream msg;
591
673
void Bind2Backend::nukeZoneRecords(BB2DomainInfo *bbd)
592
674
{
593
675
  bbd->d_loaded=0; // block further access
594
 
  bbd->d_records->clear(); // empty the vector of Bind2DNSRecords
 
676
  bbd->d_records = shared_ptr<vector<Bind2DNSRecord> > (new vector<Bind2DNSRecord>);
595
677
}
596
678
 
597
679
 
598
680
void Bind2Backend::queueReload(BB2DomainInfo *bbd)
599
681
{
600
 
  Lock l(&s_zonemap_lock);
 
682
  Lock l(&s_state_lock);
601
683
 
602
 
  s_staging_zone_map.clear(); 
 
684
  shared_ptr<State> staging(new State);
603
685
 
604
686
  // we reload *now* for the time being
605
687
 
606
688
  try {
607
 
    nukeZoneRecords(bbd);
608
 
    
609
 
    ZoneParser ZP;
610
 
    us=this;
611
 
 
612
 
    ZP.setDirectory(d_binddirectory);
613
 
    ZP.setCallback(&InsertionCallback);  
614
 
 
615
 
    s_staging_zone_map[bbd->d_id]=s_id_zone_map[bbd->d_id];
616
 
    s_staging_zone_map[bbd->d_id].d_records=shared_ptr<vector<Bind2DNSRecord> > (new vector<Bind2DNSRecord>);  // nuke it
617
 
 
618
 
    ZP.parse(bbd->d_filename, bbd->d_name, bbd->d_id);
619
 
    
620
 
    sort(s_staging_zone_map[bbd->d_id].d_records->begin(), s_staging_zone_map[bbd->d_id].d_records->end());
621
 
    s_staging_zone_map[bbd->d_id].setCtime();
 
689
    nukeZoneRecords(bbd); // ? do we need this?
 
690
    staging->id_zone_map[bbd->d_id]=s_state->id_zone_map[bbd->d_id];
 
691
    staging->id_zone_map[bbd->d_id].d_records=shared_ptr<vector<Bind2DNSRecord> > (new vector<Bind2DNSRecord>);  // nuke it
 
692
 
 
693
    ZoneParserTNG zpt(bbd->d_filename, bbd->d_name, s_binddirectory);
 
694
    DNSResourceRecord rr;
 
695
    while(zpt.get(rr)) {
 
696
      insert(staging, bbd->d_id, rr.qname, rr.qtype, rr.content, rr.ttl, rr.priority);
 
697
    }
 
698
        
 
699
    sort(staging->id_zone_map[bbd->d_id].d_records->begin(), staging->id_zone_map[bbd->d_id].d_records->end());
 
700
    staging->id_zone_map[bbd->d_id].setCtime();
622
701
    
623
702
    contents.clear();
624
703
 
625
 
    s_id_zone_map[bbd->d_id]=s_staging_zone_map[bbd->d_id]; // move over
 
704
    s_state->id_zone_map[bbd->d_id]=staging->id_zone_map[bbd->d_id]; // move over
626
705
 
627
706
    bbd->setCtime();
628
707
    // and raise d_loaded again!
636
715
    msg<<" error at "+nowTime()+" parsing '"<<bbd->d_name<<"' from file '"<<bbd->d_filename<<"': "<<ae.reason;
637
716
    bbd->d_status=msg.str();
638
717
  }
 
718
  catch(exception &ae) {
 
719
    ostringstream msg;
 
720
    msg<<" error at "+nowTime()+" parsing '"<<bbd->d_name<<"' from file '"<<bbd->d_filename<<"': "<<ae.what();
 
721
    bbd->d_status=msg.str();
 
722
  }
639
723
}
640
724
 
641
725
bool operator<(const Bind2DNSRecord &a, const string &b)
655
739
 
656
740
  string domain=toLower(qname);
657
741
 
658
 
  bool mustlog=arg().mustDo("query-logging");
 
742
  bool mustlog=::arg().mustDo("query-logging");
659
743
  if(mustlog) 
660
744
    L<<Logger::Warning<<"Lookup for '"<<qtype.getName()<<"' of '"<<domain<<"'"<<endl;
661
745
 
662
 
  while(!s_name_id_map.count(domain) && chopOff(domain));
663
 
 
664
 
  name_id_map_t::const_iterator iditer=s_name_id_map.find(domain);
665
 
 
666
 
  if(iditer==s_name_id_map.end()) {
 
746
  shared_ptr<State> state = s_state;
 
747
 
 
748
  while(!state->name_id_map.count(domain) && chopOff(domain));
 
749
 
 
750
  name_id_map_t::const_iterator iditer=state->name_id_map.find(domain);
 
751
 
 
752
  if(iditer==state->name_id_map.end()) {
667
753
    if(mustlog)
668
754
      L<<Logger::Warning<<"Found no authoritative zone for "<<qname<<endl;
669
755
    d_handle.d_list=false;
681
767
  if(strcasecmp(qname.c_str(),domain.c_str()))
682
768
    d_handle.qname=qname.substr(0,qname.size()-domain.length()-1); // strip domain name
683
769
 
684
 
  d_handle.parent=this;
685
770
  d_handle.qtype=qtype;
686
771
  d_handle.domain=qname.substr(qname.size()-domain.length());
687
772
 
688
 
  d_handle.d_records=s_id_zone_map[iditer->second].d_records; // give it a copy
689
 
  if(!d_handle.d_records->empty()) {
690
 
    BB2DomainInfo& bbd=s_id_zone_map[iditer->second];
691
 
    if(!bbd.d_loaded) {
692
 
      d_handle.reset();
693
 
      throw DBException("Zone temporarily not available (file missing, or master dead)"); // fsck
694
 
    }
 
773
  BB2DomainInfo& bbd = state->id_zone_map[iditer->second];
 
774
  if(!bbd.d_loaded) {
 
775
    d_handle.reset();
 
776
    throw DBException("Zone for '"+bbd.d_name+"' in '"+bbd.d_filename+"' temporarily not available (file missing, or master dead)"); // fsck
 
777
  }
695
778
    
696
 
    if(!bbd.current()) {
697
 
      L<<Logger::Warning<<"Zone '"<<bbd.d_name<<"' ("<<bbd.d_filename<<") needs reloading"<<endl;
698
 
      queueReload(&bbd);
699
 
      d_handle.d_records=s_id_zone_map[iditer->second].d_records; // give it a *fresh* copy
700
 
    }
 
779
  if(!bbd.current()) {
 
780
    L<<Logger::Warning<<"Zone '"<<bbd.d_name<<"' ("<<bbd.d_filename<<") needs reloading"<<endl;
 
781
    queueReload(&bbd);  // how can this be safe - ok, everybody should have their own reference counted copy of 'records'
 
782
    state = s_state;
701
783
  }
702
 
  else {
 
784
 
 
785
  d_handle.d_records = state->id_zone_map[iditer->second].d_records; // give it a reference counted copy
 
786
  
 
787
  if(d_handle.d_records->empty())
703
788
    DLOG(L<<"Query with no results"<<endl);
704
 
  }
705
789
 
706
790
  pair<vector<Bind2DNSRecord>::const_iterator, vector<Bind2DNSRecord>::const_iterator> range;
707
791
 
717
801
  else {
718
802
    d_handle.d_iter=range.first;
719
803
    d_handle.d_end_iter=range.second;
 
804
    d_handle.mustlog = mustlog;
 
805
 
720
806
  }
721
807
 
722
808
  d_handle.d_list=false;
724
810
 
725
811
Bind2Backend::handle::handle()
726
812
{
727
 
  //  d_records=0;
728
 
  count=0;
 
813
  mustlog=false;
729
814
}
730
815
 
731
816
bool Bind2Backend::get(DNSResourceRecord &r)
736
821
  if(!d_handle.get(r)) {
737
822
    d_handle.reset();
738
823
 
739
 
    if(arg().mustDo("query-logging"))
 
824
    if(::arg().mustDo("query-logging"))
740
825
      L<<"End of answers"<<endl;
741
826
 
742
827
    return false;
743
828
  }
744
 
  if(arg().mustDo("query-logging"))
745
 
    L<<"Returning: '"<<r.qtype.getName()<<"' of '"<<r.qname<<"', content: '"<<r.content<<"'"<<endl;
 
829
  if(d_handle.mustlog)
 
830
    L<<Logger::Warning<<"Returning: '"<<r.qtype.getName()<<"' of '"<<r.qname<<"', content: '"<<r.content<<"', prio: "<<r.priority<<endl;
746
831
  return true;
747
832
}
748
833
 
786
871
 
787
872
bool Bind2Backend::list(const string &target, int id)
788
873
{
789
 
  if(!s_id_zone_map.count(id))
 
874
  shared_ptr<State> state = s_state;
 
875
  if(!state->id_zone_map.count(id))
790
876
    return false;
791
877
 
792
878
  d_handle.reset(); 
793
879
  DLOG(L<<"Bind2Backend constructing handle for list of "<<id<<endl);
794
880
 
795
 
  d_handle.d_qname_iter=s_id_zone_map[id].d_records->begin();
796
 
  d_handle.d_qname_end=s_id_zone_map[id].d_records->end();   // iter now points to a vector of pointers to vector<BBResourceRecords>
797
 
 
798
 
  d_handle.d_records=s_id_zone_map[id].d_records; // give it a copy --- WHY??? XXX FIXME
799
 
 
800
 
  d_handle.parent=this;
 
881
  d_handle.d_records=state->id_zone_map[id].d_records; // give it a copy, which will stay around
 
882
  d_handle.d_qname_iter= d_handle.d_records->begin();
 
883
  d_handle.d_qname_end=d_handle.d_records->end();   // iter now points to a vector of pointers to vector<BBResourceRecords>
 
884
 
801
885
  d_handle.id=id;
802
886
  d_handle.d_list=true;
803
887
  return true;
822
906
 
823
907
bool Bind2Backend::isMaster(const string &name, const string &ip)
824
908
{
825
 
  for(id_zone_map_t::iterator j=us->s_id_zone_map.begin();j!=us->s_id_zone_map.end();++j) 
826
 
    if(j->second.d_name==name)
827
 
      return j->second.d_master==ip;
 
909
  for(id_zone_map_t::iterator j=s_state->id_zone_map.begin();j!=s_state->id_zone_map.end();++j) {
 
910
    if(j->second.d_name==name) {
 
911
      for(vector<string>::const_iterator iter = j->second.d_masters.begin(); iter != j->second.d_masters.end(); ++iter)
 
912
        if(*iter==ip)
 
913
          return true;
 
914
    }
 
915
  }
828
916
  return false;
829
917
}
830
918
 
867
955
bool Bind2Backend::createSlaveDomain(const string &ip, const string &domain, const string &account)
868
956
{
869
957
  // Interference with loadConfig(), use locking
870
 
  Lock l(&s_zonemap_lock);
 
958
  Lock l(&s_state_lock);
871
959
 
872
960
  string filename = getArg("supermaster-destdir")+'/'+domain;
873
961
  
893
981
  int newid=0;
894
982
  // Find a free zone id nr.  
895
983
  
896
 
  if (!s_id_zone_map.empty()) {
897
 
    id_zone_map_t::reverse_iterator i = s_id_zone_map.rbegin();
 
984
  if (!s_state->id_zone_map.empty()) {
 
985
    id_zone_map_t::reverse_iterator i = s_state->id_zone_map.rbegin();
898
986
    newid = i->second.d_id + 1;
899
987
  }
900
988
  
901
 
  BB2DomainInfo &bbd = s_id_zone_map[newid];
 
989
  BB2DomainInfo &bbd = s_state->id_zone_map[newid];
902
990
 
903
991
  bbd.d_records = shared_ptr<vector<Bind2DNSRecord> >(new vector<Bind2DNSRecord>);
904
992
  bbd.d_name = domain;
905
993
  bbd.setCheckInterval(getArgAsNum("check-interval"));
906
 
  bbd.d_master = ip;
 
994
  bbd.d_masters.push_back(ip);
907
995
  bbd.d_filename = filename;
908
996
 
909
 
  s_name_id_map[domain] = bbd.d_id;
 
997
  s_state->name_id_map[domain] = bbd.d_id;
910
998
  
911
999
  return true;
912
1000
}
919
1007
      void declareArguments(const string &suffix="")
920
1008
      {
921
1009
         declare(suffix,"config","Location of named.conf","");
922
 
         declare(suffix,"example-zones","Install example zones","no");
 
1010
         //         declare(suffix,"example-zones","Install example zones","no");
923
1011
         declare(suffix,"check-interval","Interval for zonefile changes","0");
924
1012
         declare(suffix,"supermaster-config","Location of (part of) named.conf where pdns can write zone-statements to","");
925
1013
         declare(suffix,"supermasters","List of IP-addresses of supermasters","");
926
 
         declare(suffix,"supermaster-destdir","Destination directory for newly added slave zones",arg()["config-dir"]);
 
1014
         declare(suffix,"supermaster-destdir","Destination directory for newly added slave zones",::arg()["config-dir"]);
927
1015
      }
928
1016
 
929
1017
      DNSBackend *make(const string &suffix="")