root/trunk/pdns/pdns/communicator.cc @ 155

Revision 155, 12.3 KB (checked in by ahu, 10 years ago)

work on zone2sql dot stripping
local-query-address

  • Property svn:eol-style set to native
  • Property svn:keywords set to author date id revision
Line 
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 as published by
7    the Free Software Foundation; either version 2 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18*/
19#include "utility.hh"
20#include <errno.h>
21#include "communicator.hh"
22#include <set>
23
24#include "dnsbackend.hh"
25#include "ueberbackend.hh"
26#include "packethandler.hh"
27#include "resolver.hh"
28#include "logger.hh"
29#include "dns.hh"
30#include "arguments.hh"
31#include "session.hh"
32
33
34void CommunicatorClass::addSuckRequest(const string &domain, const string &master)
35{
36  Lock l(&d_lock);
37 
38  SuckRequest sr;
39  sr.domain = domain;
40  sr.master = master;
41
42  d_suckdomains.push(sr);
43 
44  d_suck_sem.post();
45  d_any_sem.post();
46}
47
48
49void CommunicatorClass::suck(const string &domain,const string &remote)
50{
51  u_int32_t domain_id;
52  PacketHandler P;
53
54  DomainInfo di;
55  di.backend=0;
56  bool first=true;   
57  try {
58    Resolver resolver;
59    resolver.axfr(remote,domain.c_str());
60
61    UeberBackend *B=dynamic_cast<UeberBackend *>(P.getBackend());
62
63    if(!B->getDomainInfo(domain, di) || !di.backend) {
64      L<<Logger::Error<<"Can't determine backend for domain '"<<domain<<"'"<<endl;
65      return;
66    }
67    domain_id=di.id;
68
69    Resolver::res_t recs;
70
71    while(resolver.axfrChunk(recs)) {
72      if(first) {
73        L<<Logger::Error<<"AXFR started for '"<<domain<<"', transaction started"<<endl;
74        di.backend->startTransaction(domain, domain_id);
75        first=false;
76      }
77      for(Resolver::res_t::iterator i=recs.begin();i!=recs.end();++i) {
78        if(!endsOn(i->qname, domain)) { 
79          L<<Logger::Error<<"Remote "<<remote<<" sneaked in out-of-zone data '"<<i->qname<<"' during AXFR of zone '"<<domain<<"'"<<endl;
80          di.backend->abortTransaction();
81          return;
82        }
83        i->domain_id=domain_id;
84        di.backend->feedRecord(*i);
85      }
86    }
87    di.backend->commitTransaction();
88    di.backend->setFresh(domain_id);
89    L<<Logger::Error<<"AXFR done for '"<<domain<<"', zone committed"<<endl;
90  }
91  catch(DBException &re) {
92    L<<Logger::Error<<"Unable to feed record during incoming AXFR of '"+domain+"': "<<re.reason<<endl;
93    if(di.backend && !first) {
94      L<<Logger::Error<<"Aborting possible open transaction for domain '"<<domain<<"' AXFR"<<endl;
95      di.backend->abortTransaction();
96    }
97  }
98  catch(ResolverException &re) {
99    L<<Logger::Error<<"Unable to AXFR zone '"+domain+"': "<<re.reason<<endl;
100    if(di.backend && !first) {
101      L<<Logger::Error<<"Aborting possible open transaction for domain '"<<domain<<"' AXFR"<<endl;
102      di.backend->abortTransaction();
103    }
104  }
105}
106
107class FindNS
108{
109public:
110  vector<string>lookup(const string &name, DNSBackend *B)
111  {
112    vector<string>addresses;
113    struct hostent *h;
114    h=gethostbyname(name.c_str());
115
116    if(h) {
117      for(char **h_addr_list=h->h_addr_list;*h_addr_list;++h_addr_list) {
118        ostringstream os;
119        unsigned char *p=reinterpret_cast<unsigned char *>(*h_addr_list);
120        os<<(int)*p++<<".";
121        os<<(int)*p++<<".";
122        os<<(int)*p++<<".";
123        os<<(int)*p++;
124
125        addresses.push_back(os.str());
126      }
127    }
128
129    B->lookup(QType(QType::A),name);
130    DNSResourceRecord rr;
131    while(B->get(rr)) 
132      addresses.push_back(rr.content);   // SOL if you have a CNAME for an NS
133
134    return addresses;
135  }
136}d_fns;
137
138void CommunicatorClass::queueNotifyDomain(const string &domain, DNSBackend *B)
139{
140  set<string> ips;
141 
142  DNSResourceRecord rr;
143  set<string>nsset;
144
145  B->lookup(QType(QType::NS),domain);
146  while(B->get(rr)) 
147    nsset.insert(rr.content);
148 
149  for(set<string>::const_iterator j=nsset.begin();j!=nsset.end();++j) {
150    vector<string>nsips=d_fns.lookup(*j, B);
151    if(nsips.empty())
152      L<<Logger::Warning<<"Unable to queue notification of domain '"<<domain<<"': nameservers do not resolve!"<<endl;
153    for(vector<string>::const_iterator k=nsips.begin();k!=nsips.end();++k)
154      ips.insert(*k);
155  }
156 
157  // make calls to d_nq.add(domain, ip);
158  for(set<string>::const_iterator j=ips.begin();j!=ips.end();++j) {
159    L<<Logger::Warning<<"Queued notification of domain '"<<domain<<"' to "<<*j<<endl;
160    d_nq.add(domain,*j);
161  }
162 
163  set<string>alsoNotify;
164  B->alsoNotifies(domain, &alsoNotify);
165 
166  for(set<string>::const_iterator j=alsoNotify.begin();j!=alsoNotify.end();++j) {
167    L<<Logger::Warning<<"Queued also-notification of domain '"<<domain<<"' to "<<*j<<endl;
168    d_nq.add(domain,*j);
169  }
170}
171
172bool CommunicatorClass::notifyDomain(const string &domain)
173{
174  DomainInfo di;
175  PacketHandler P;
176  if(!P.getBackend()->getDomainInfo(domain, di)) {
177    L<<Logger::Error<<"No such domain '"<<domain<<"' in our database"<<endl;
178    return false;
179  }
180  queueNotifyDomain(domain, P.getBackend());
181  // call backend and tell them we sent out the notification - even though that is premature   
182  di.backend->setNotified(di.id, di.serial);
183
184  return true; 
185}
186
187
188void CommunicatorClass::masterUpdateCheck(PacketHandler *P)
189{
190  if(!arg().mustDo("master"))
191    return; 
192
193  UeberBackend *B=dynamic_cast<UeberBackend *>(P->getBackend());
194  vector<DomainInfo> cmdomains;
195  B->getUpdatedMasters(&cmdomains);
196 
197  if(cmdomains.empty()) {
198    if(d_masterschanged)
199      L<<Logger::Error<<"No master domains need notifications"<<endl;
200    d_masterschanged=false;
201  }
202  else {
203    d_masterschanged=true;
204    L<<Logger::Error<<cmdomains.size()<<" domain"<<(cmdomains.size()>1 ? "s" : "")<<" for which we are master need"<<
205      (cmdomains.size()>1 ? "" : "s")<<
206      " notifications"<<endl;
207  }
208
209  // figure out A records of everybody needing notification
210  // do this via the FindNS class, d_fns
211 
212  for(vector<DomainInfo>::const_iterator i=cmdomains.begin();i!=cmdomains.end();++i) {
213
214    queueNotifyDomain(i->zone,P->getBackend());
215    i->backend->setNotified(i->id,i->serial); 
216  }
217}
218
219void CommunicatorClass::slaveRefresh(PacketHandler *P)
220{
221  UeberBackend *B=dynamic_cast<UeberBackend *>(P->getBackend());
222  vector<DomainInfo> sdomains;
223  B->getUnfreshSlaveInfos(&sdomains);
224 
225  if(sdomains.empty())
226  {
227    if(d_slaveschanged)
228      L<<Logger::Error<<"All slave domains are fresh"<<endl;
229    d_slaveschanged=false;
230    return;
231  }
232  else 
233    L<<Logger::Error<<sdomains.size()<<" slave domain"<<(sdomains.size()>1 ? "s" : "")<<" need"<<
234      (sdomains.size()>1 ? "" : "s")<<
235      " checking"<<endl;
236 
237  for(vector<DomainInfo>::const_iterator i=sdomains.begin();i!=sdomains.end();++i) {
238    d_slaveschanged=true;
239    u_int32_t ourserial=i->serial,theirserial=0;
240
241    try {
242      Resolver resolver;
243      int res=resolver.getSoaSerial(i->master,i->zone, &theirserial);
244      if(res<=0) {
245        L<<Logger::Error<<"Unable to determine SOA serial for "<<i->zone<<" at "<<i->master<<endl;
246        continue;
247      }
248     
249      if(theirserial<i->serial) {
250        L<<Logger::Error<<"Domain "<<i->zone<<" more recent than master, our serial "<<ourserial<<" > their serial "<<theirserial<<endl;
251        i->backend->setFresh(i->id);
252      }
253      else if(theirserial==i->serial) {
254        L<<Logger::Warning<<"Domain "<<i->zone<<" is fresh"<<endl;
255        i->backend->setFresh(i->id);
256      }
257      else {
258        L<<Logger::Error<<"Domain "<<i->zone<<" is stale, master serial "<<theirserial<<", our serial "<<i->serial<<endl;
259        addSuckRequest(i->zone,i->master);
260      }
261    }
262    catch(ResolverException &re) {
263      L<<Logger::Error<<"Error trying to retrieve/refresh '"+i->zone+"': "+re.reason<<endl;
264    }
265  }
266} 
267
268
269
270int CommunicatorClass::doNotifications()
271{
272  struct sockaddr_in from;
273  Utility::socklen_t fromlen=sizeof(from);
274  char buffer[1500];
275  int size;
276  static Resolver d_nresolver;
277  // receive incoming notifications on the nonblocking socket and take them off the list
278
279#ifndef WIN32
280  while((size=recvfrom(d_nsock,buffer,sizeof(buffer),0,(struct sockaddr *)&from,&fromlen))>0) {
281#else
282  while((size=recvfrom(d_nsock,buffer,sizeof(buffer),0,(struct sockaddr *)&from,&fromlen))>0) {
283#endif
284
285
286
287    DNSPacket p;
288
289    p.setRemote((struct sockaddr *)&from, fromlen);
290
291    if(p.parse(buffer,size)<0) {
292      L<<Logger::Warning<<"Unable to parse SOA notification answer from "<<p.getRemote()<<endl;
293      continue;
294    }
295
296    if(p.d.rcode)
297      L<<Logger::Warning<<"Received unsuccesful notification report for '"<<p.qdomain<<"' from "<<p.getRemote()<<", rcode: "<<p.d.rcode<<endl;     
298   
299    if(d_nq.removeIf(p.getRemote(), p.d.id, p.qdomain))
300      L<<Logger::Warning<<"Removed from notification list: '"<<p.qdomain<<"' to "<<p.getRemote()<< (p.d.rcode ? "" : " (was acknowledged)")<<endl;     
301    else
302      L<<Logger::Warning<<"Received spurious notify answer for '"<<p.qdomain<<"' from "<<p.getRemote()<<endl;     
303  }
304
305  // send out possible new notifications
306  string domain, ip;
307  u_int16_t id;
308
309  bool purged;
310  while(d_nq.getOne(domain, ip, &id, purged)) {
311    if(!purged) {
312      d_nresolver.notify(d_nsock,domain,ip,id);
313      drillHole(domain,ip);
314    }
315    else
316      L<<Logger::Error<<Logger::NTLog<<"Notification for "<<domain<<" to "<<ip<<" failed after retries"<<endl;
317  }
318
319  return d_nq.earliest();
320}
321
322void CommunicatorClass::drillHole(const string &domain, const string &ip)
323{
324  Lock l(&d_holelock);
325  d_holes[make_pair(domain,ip)]=time(0);
326}
327
328bool CommunicatorClass::justNotified(const string &domain, const string &ip)
329{
330  Lock l(&d_holelock);
331  if(d_holes.find(make_pair(domain,ip))==d_holes.end()) // no hole
332    return false;
333
334  if(d_holes[make_pair(domain,ip)]>time(0)-900)    // recent hole
335    return true;
336
337  // do we want to purge this? XXX FIXME
338  return false;
339}
340
341void CommunicatorClass::makeNotifySocket()
342{
343  if((d_nsock=socket(AF_INET, SOCK_DGRAM,0))<0)
344    throw AhuException(string("notification socket: ")+strerror(errno));
345
346  struct sockaddr_in sin;
347  memset((char *)&sin,0, sizeof(sin));
348 
349  sin.sin_family = AF_INET;
350
351  // Bind to a specific IP (query-local-address) if specified
352  string querylocaladdress(arg()["query-local-address"]);
353  if (querylocaladdress=="") {
354    sin.sin_addr.s_addr = INADDR_ANY;
355  }
356  else
357  {
358    struct hostent *h=0;
359    h=gethostbyname(querylocaladdress.c_str());
360    if(!h) {
361      Utility::closesocket(d_nsock);
362      d_nsock=-1;       
363      throw AhuException("Unable to resolve query local address");
364    }
365
366    sin.sin_addr.s_addr = *(int*)h->h_addr;
367  }
368 
369  int n=0;
370  for(;n<10;n++) {
371    sin.sin_port = htons(10000+(Utility::random()%50000));
372   
373    if(bind(d_nsock, (struct sockaddr *)&sin, sizeof(sin)) >= 0) 
374      break;
375  }
376  if(n==10) {
377    Utility::closesocket(d_nsock);
378    d_nsock=-1;
379    throw AhuException(string("binding notify socket: ")+strerror(errno));
380  }
381  if( !Utility::setNonBlocking( d_nsock ))
382    throw AhuException(string("error getting or setting notify socket non-blocking: ")+strerror(errno));
383
384}
385
386void CommunicatorClass::notify(const string &domain, const string &ip)
387{
388  d_nq.add(domain,ip);
389
390  d_any_sem.post();
391}
392
393void CommunicatorClass::mainloop(void)
394{
395  try {
396#ifndef WIN32
397    signal(SIGPIPE,SIG_IGN);
398#endif // WIN32
399    L<<Logger::Error<<"Master/slave communicator launching"<<endl;
400    PacketHandler P;
401    d_tickinterval=arg().asNum("slave-cycle-interval");
402    makeNotifySocket();
403
404    int rc;
405    time_t next;
406
407    int tick;
408
409    for(;;) {
410      slaveRefresh(&P);
411      masterUpdateCheck(&P);
412
413      tick=min(doNotifications(),
414               d_tickinterval);
415
416      next=time(0)+tick;
417
418      while(time(0)<next) {
419        rc=d_any_sem.tryWait();
420
421        if(rc)
422          Utility::sleep(1);
423        else { 
424          if(!d_suck_sem.tryWait()) {
425            SuckRequest sr;
426            {
427              Lock l(&d_lock);
428              sr=d_suckdomains.front();
429              d_suckdomains.pop();
430            }
431            suck(sr.domain,sr.master);
432          }
433        }
434        // this gets executed at least once every second
435        doNotifications();
436      }
437    }
438  }
439  catch(AhuException &ae) {
440    L<<Logger::Error<<"Communicator thread died because of error: "<<ae.reason<<endl;
441    Utility::sleep(1);
442    exit(0);
443  }
444  catch(exception &e) {
445    L<<Logger::Error<<"Communicator thread died because of STL error: "<<e.what()<<endl;
446    exit(0);
447  }
448  catch( ... )
449  {
450    L << Logger::Error << "Communicator caught unknown exception." << endl;
451    exit( 0 );
452  }
453}
454
Note: See TracBrowser for help on using the browser.