2 PowerDNS Versatile Database Driven Nameserver
3 Copyright (C) 2002-2012 PowerDNS.COM BV
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;
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.
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 #include "packetcache.hh"
20 #include "dnssecinfra.hh"
21 #include "dnsseckeeper.hh"
24 #include "communicator.hh"
26 #include <boost/utility.hpp>
27 #include "dnsbackend.hh"
28 #include "ueberbackend.hh"
29 #include "packethandler.hh"
30 #include "resolver.hh"
33 #include "arguments.hh"
35 #include "packetcache.hh"
36 #include <boost/foreach.hpp>
37 #include <boost/lexical_cast.hpp>
39 #include "inflighter.cc"
40 #include "lua-auth.hh"
41 #include "namespaces.hh"
42 #include "common_startup.hh"
43 #include <boost/scoped_ptr.hpp>
44 using boost::scoped_ptr
;
47 void CommunicatorClass::addSuckRequest(const string
&domain
, const string
&master
)
53 pair
<UniQueue::iterator
, bool> res
;
55 res
=d_suckdomains
.push_back(sr
);
62 void CommunicatorClass::suck(const string
&domain
,const string
&remote
)
64 L
<<Logger::Error
<<"Initiating transfer of '"<<domain
<<"' from remote '"<<remote
<<"'"<<endl
;
66 PacketHandler P
; // fresh UeberBackend
72 UeberBackend
*B
=dynamic_cast<UeberBackend
*>(P
.getBackend()); // copy of the same UeberBackend
73 NSEC3PARAMRecordContent ns3pr
, hadNs3pr
;
74 bool narrow
, hadNarrow
=false;
75 DNSSECKeeper
dk (B
); // reuse our backend for DNSSECKeeper
76 bool dnssecZone
= false;
78 if(dk
.isSecuredZone(domain
)) {
80 haveNSEC3
=dk
.getNSEC3PARAM(domain
, &ns3pr
, &narrow
);
87 const bool hadNSEC3
= haveNSEC3
;
88 const bool hadPresigned
= dk
.isPresigned(domain
);
89 const bool hadDnssecZone
= dnssecZone
;
91 if(!B
->getDomainInfo(domain
, di
) || !di
.backend
) { // di.backend and B are mostly identical
92 L
<<Logger::Error
<<"Can't determine backend for domain '"<<domain
<<"'"<<endl
;
98 set
<string
> nsset
, qnames
;
100 ComboAddress
raddr(remote
, 53);
102 string tsigkeyname
, tsigalgorithm
, tsigsecret
;
104 if(dk
.getTSIGForAccess(domain
, remote
, &tsigkeyname
)) {
106 if(B
->getTSIGKey(tsigkeyname
, &tsigalgorithm
, &tsigsecret64
))
108 B64Decode(tsigsecret64
, tsigsecret
);
112 L
<<Logger::Error
<<"TSIG key '"<<tsigkeyname
<<"' not found, ignoring 'AXFR-MASTER-TSIG' for domain '"<<domain
<<"'"<<endl
;
117 scoped_ptr
<AuthLua
> pdl
;
118 vector
<string
> scripts
;
119 if(B
->getDomainMetadata(domain
, "LUA-AXFR-SCRIPT", scripts
) && !scripts
.empty()) {
121 pdl
.reset(new AuthLua(scripts
[0]));
122 L
<<Logger::Info
<<"Loaded Lua script '"<<scripts
[0]<<"' to edit the incoming AXFR of '"<<domain
<<"'"<<endl
;
124 catch(std::exception
& e
) {
125 L
<<Logger::Error
<<"Failed to load Lua editing script '"<<scripts
[0]<<"' for incoming AXFR of '"<<domain
<<"': "<<e
.what()<<endl
;
130 vector
<string
> localaddr
;
133 if(B
->getDomainMetadata(domain
, "AXFR-SOURCE", localaddr
) && !localaddr
.empty()) {
135 laddr
= ComboAddress(localaddr
[0]);
136 L
<<Logger::Info
<<"AXFR source for domain '"<<domain
<<"' set to "<<localaddr
[0]<<endl
;
138 catch(std::exception
& e
) {
139 L
<<Logger::Error
<<"Failed to load AXFR source '"<<localaddr
[0]<<"' for incoming AXFR of '"<<domain
<<"': "<<e
.what()<<endl
;
143 laddr
.sin4
.sin_family
= 0;
146 AXFRRetriever
retriever(raddr
, domain
.c_str(), tsigkeyname
, tsigalgorithm
, tsigsecret
,
147 (laddr
.sin4
.sin_family
== 0) ? NULL
: &laddr
);
149 bool gotPresigned
= false;
150 bool gotNSEC3
= false;
151 bool gotOptOutFlag
= false;
152 unsigned int soa_serial
= 0;
153 vector
<DNSResourceRecord
> rrs
;
155 while(retriever
.getChunk(recs
)) {
157 L
<<Logger::Error
<<"AXFR started for '"<<domain
<<"'"<<endl
;
161 for(Resolver::res_t::iterator i
=recs
.begin();i
!=recs
.end();++i
) {
162 if(i
->qtype
.getCode() == QType::OPT
|| i
->qtype
.getCode() == QType::TSIG
) // ignore EDNS0 & TSIG
165 if(!endsOn(i
->qname
, domain
)) {
166 L
<<Logger::Error
<<"Remote "<<remote
<<" tried to sneak in out-of-zone data '"<<i
->qname
<<"'|"<<i
->qtype
.getName()<<" during AXFR of zone '"<<domain
<<"', ignoring"<<endl
;
170 if (i
->qtype
.getCode() == QType::NSEC3PARAM
) {
171 ns3pr
= NSEC3PARAMRecordContent(i
->content
);
173 dnssecZone
= haveNSEC3
= gotPresigned
= gotNSEC3
= true;
175 } else if (i
->qtype
.getCode() == QType::NSEC3
) {
176 dnssecZone
= gotPresigned
= true;
177 NSEC3RecordContent
ns3rc(i
->content
);
178 gotOptOutFlag
= ns3rc
.d_flags
& 1;
179 if (ns3rc
.d_set
.count(QType::NS
) && !pdns_iequals(i
->qname
, domain
))
180 secured
.insert(toLower(makeRelative(i
->qname
, domain
)));
182 } else if (i
->qtype
.getCode() == QType::NSEC
) {
183 dnssecZone
= gotPresigned
= true;
187 if(i
->qtype
.getCode() == QType::SOA
) {
189 continue; //skip the last SOA
191 fillSOAData(i
->content
,sd
);
192 soa_serial
= sd
.serial
;
195 i
->domain_id
=domain_id
;
198 if(i
->qtype
.getCode()>=60000)
199 throw DBException("Database can't store unknown record type "+lexical_cast
<string
>(i
->qtype
.getCode()-1024));
202 vector
<DNSResourceRecord
> out
;
203 if(pdl
&& pdl
->axfrfilter(raddr
, domain
, *i
, out
)) {
204 BOOST_FOREACH(const DNSResourceRecord
& rr
, out
) {
214 BOOST_FOREACH(const DNSResourceRecord
& rr
, rrs
) {
215 if(rr
.qtype
.getCode() == QType::NS
&& !pdns_iequals(rr
.qname
, domain
))
216 nsset
.insert(rr
.qname
);
217 qnames
.insert(rr
.qname
);
222 L
<<Logger::Info
<<"Adding NSEC ordering information"<<endl
;
224 L
<<Logger::Info
<<"Adding NSEC3 hashed ordering information for '"<<domain
<<"'"<<endl
;
226 L
<<Logger::Info
<<"Erasing NSEC3 ordering since we are narrow, only setting 'auth' fields"<<endl
;
229 if (hadPresigned
&& !gotNSEC3
) { // not sure why this is here
230 // we only had NSEC3 because we were a presigned zone...
235 L
<<Logger::Error
<<"Transaction started for '"<<domain
<<"'"<<endl
;
236 di
.backend
->startTransaction(domain
, domain_id
);
239 uint32_t maxent
= ::arg().asNum("max-ent-entries");
240 string ordername
, shorter
;
241 set
<string
> nonterm
, rrterm
;
243 BOOST_FOREACH(DNSResourceRecord
& rr
, rrs
) {
245 // Figure out auth and ents
251 if (!qnames
.count(shorter
) && !nonterm
.count(shorter
) && !rrterm
.count(shorter
))
252 rrterm
.insert(shorter
);
254 if(nsset
.count(shorter
) && rr
.qtype
.getCode() != QType::DS
) {
258 if (pdns_iequals(shorter
, domain
)) // stop at apex
260 }while(chopOff(shorter
));
262 // Insert ents for auth rrs
263 if(doent
&& rr
.auth
) {
264 nonterm
.insert(rrterm
.begin(), rrterm
.end());
265 if(nonterm
.size() > maxent
) {
266 L
<<Logger::Error
<<"AXFR zone "<<domain
<<" has too many empty non terminals."<<endl
;
272 // RRSIG is always auth, even inside a delegation
273 if (rr
.qtype
.getCode() == QType::RRSIG
)
276 // Add ordername and insert record
277 if (dnssecZone
&& rr
.qtype
.getCode() != QType::RRSIG
) {
280 ordername
=toLower(toBase32Hex(hashQNameWithSalt(ns3pr
.d_iterations
, ns3pr
.d_salt
, rr
.qname
)));
281 if(!narrow
&& (rr
.auth
|| (rr
.qtype
.getCode() == QType::NS
&& (!gotOptOutFlag
|| secured
.count(ordername
))))) {
282 di
.backend
->feedRecord(rr
, &ordername
);
284 di
.backend
->feedRecord(rr
);
287 if (rr
.auth
|| rr
.qtype
.getCode() == QType::NS
) {
288 ordername
=toLower(labelReverse(makeRelative(rr
.qname
, domain
)));
289 di
.backend
->feedRecord(rr
, &ordername
);
291 di
.backend
->feedRecord(rr
);
294 di
.backend
->feedRecord(rr
);
297 // Insert empty non-terminals
298 if(doent
&& !nonterm
.empty()) {
300 di
.backend
->feedEnts3(domain_id
, domain
, nonterm
, ns3pr
.d_iterations
, ns3pr
.d_salt
, narrow
);
302 di
.backend
->feedEnts(domain_id
, nonterm
);
305 // now we also need to update the presigned flag and NSEC3PARAM
308 if (!hadDnssecZone
&& !hadPresigned
) {
309 // zone is now presigned
310 dk
.setPresigned(domain
);
313 if (hadPresigned
|| !hadDnssecZone
)
315 // this is a presigned zone, update NSEC3PARAM
317 ns3pr
.d_flags
= gotOptOutFlag
? 1 : 0;
318 // only update if there was a change
319 if (!hadNSEC3
|| (narrow
!= hadNarrow
) ||
320 (ns3pr
.d_algorithm
!= hadNs3pr
.d_algorithm
) ||
321 (ns3pr
.d_flags
!= hadNs3pr
.d_flags
) ||
322 (ns3pr
.d_iterations
!= hadNs3pr
.d_iterations
) ||
323 (ns3pr
.d_salt
!= hadNs3pr
.d_salt
)) {
324 dk
.setNSEC3PARAM(domain
, ns3pr
, narrow
);
326 } else if (hadNSEC3
) {
327 dk
.unsetNSEC3PARAM(domain
);
330 } else if (hadPresigned
) {
331 // zone is no longer presigned
332 dk
.unsetPresigned(domain
);
333 dk
.unsetNSEC3PARAM(domain
);
336 di
.backend
->commitTransaction();
337 di
.backend
->setFresh(domain_id
);
338 PC
.purge(domain
+"$");
341 L
<<Logger::Error
<<"AXFR done for '"<<domain
<<"', zone committed with serial number "<<soa_serial
<<endl
;
342 if(::arg().mustDo("slave-renotify"))
343 notifyDomain(domain
);
345 catch(DBException
&re
) {
346 L
<<Logger::Error
<<"Unable to feed record during incoming AXFR of '"+domain
+"': "<<re
.reason
<<endl
;
347 if(di
.backend
&& !first
) {
348 L
<<Logger::Error
<<"Aborting possible open transaction for domain '"<<domain
<<"' AXFR"<<endl
;
349 di
.backend
->abortTransaction();
352 catch(MOADNSException
&re
) {
353 L
<<Logger::Error
<<"Unable to parse record during incoming AXFR of '"+domain
+"' (MOADNSException): "<<re
.what()<<endl
;
354 if(di
.backend
&& !first
) {
355 L
<<Logger::Error
<<"Aborting possible open transaction for domain '"<<domain
<<"' AXFR"<<endl
;
356 di
.backend
->abortTransaction();
359 catch(std::exception
&re
) {
360 L
<<Logger::Error
<<"Unable to parse record during incoming AXFR of '"+domain
+"' (std::exception): "<<re
.what()<<endl
;
361 if(di
.backend
&& !first
) {
362 L
<<Logger::Error
<<"Aborting possible open transaction for domain '"<<domain
<<"' AXFR"<<endl
;
363 di
.backend
->abortTransaction();
366 catch(ResolverException
&re
) {
367 L
<<Logger::Error
<<"Unable to AXFR zone '"+domain
+"' from remote '"<<remote
<<"' (resolver): "<<re
.reason
<<endl
;
368 if(di
.backend
&& !first
) {
369 L
<<Logger::Error
<<"Aborting possible open transaction for domain '"<<domain
<<"' AXFR"<<endl
;
370 di
.backend
->abortTransaction();
373 catch(PDNSException
&ae
) {
374 L
<<Logger::Error
<<"Unable to AXFR zone '"+domain
+"' from remote '"<<remote
<<"' (PDNSException): "<<ae
.reason
<<endl
;
375 if(di
.backend
&& !first
) {
376 L
<<Logger::Error
<<"Aborting possible open transaction for domain '"<<domain
<<"' AXFR"<<endl
;
377 di
.backend
->abortTransaction();
384 struct timeval query_ttd
;
388 struct DomainNotificationInfo
392 string tsigkeyname
, tsigalgname
, tsigsecret
;
397 struct SlaveSenderReceiver
399 typedef pair
<string
, uint16_t> Identifier
;
402 uint32_t theirSerial
;
403 uint32_t theirInception
;
404 uint32_t theirExpire
;
407 map
<uint32_t, Answer
> d_freshness
;
409 SlaveSenderReceiver()
413 void deliverTimeout(const Identifier
& i
)
417 Identifier
send(DomainNotificationInfo
& dni
)
419 random_shuffle(dni
.di
.masters
.begin(), dni
.di
.masters
.end());
421 ComboAddress
remote(*dni
.di
.masters
.begin());
422 return make_pair(dni
.di
.zone
,
423 d_resolver
.sendResolve(ComboAddress(*dni
.di
.masters
.begin(), 53),
426 dni
.dnssecOk
, dni
.tsigkeyname
, dni
.tsigalgname
, dni
.tsigsecret
)
429 catch(PDNSException
& e
) {
430 throw runtime_error("While attempting to query freshness of '"+dni
.di
.zone
+"': "+e
.reason
);
434 bool receive(Identifier
& id
, Answer
& a
)
436 if(d_resolver
.tryGetSOASerial(&id
.first
, &a
.theirSerial
, &a
.theirInception
, &a
.theirExpire
, &id
.second
)) {
442 void deliverAnswer(DomainNotificationInfo
& dni
, const Answer
& a
, unsigned int usec
)
444 d_freshness
[dni
.di
.id
]=a
;
450 void CommunicatorClass::addSlaveCheckRequest(const DomainInfo
& di
, const ComboAddress
& remote
)
453 DomainInfo ours
= di
;
455 d_tocheck
.insert(ours
);
456 d_any_sem
.post(); // kick the loop!
459 void CommunicatorClass::addTrySuperMasterRequest(DNSPacket
*p
)
463 d_potentialsupermasters
.push_back(ours
);
464 d_any_sem
.post(); // kick the loop!
467 void CommunicatorClass::slaveRefresh(PacketHandler
*P
)
469 UeberBackend
*B
=dynamic_cast<UeberBackend
*>(P
->getBackend());
470 vector
<DomainInfo
> rdomains
;
471 vector
<DomainNotificationInfo
> sdomains
; // the bool is for 'presigned'
472 vector
<DNSPacket
> trysuperdomains
;
476 rdomains
.insert(rdomains
.end(), d_tocheck
.begin(), d_tocheck
.end());
478 trysuperdomains
.insert(trysuperdomains
.end(), d_potentialsupermasters
.begin(), d_potentialsupermasters
.end());
479 d_potentialsupermasters
.clear();
482 BOOST_FOREACH(DNSPacket
& dp
, trysuperdomains
) {
484 res
=P
->trySuperMasterSynchronous(&dp
);
486 DNSPacket
*r
=dp
.replyPacket();
488 r
->setOpcode(Opcode::Notify
);
494 if(rdomains
.empty()) // if we have priority domains, check them first
495 B
->getUnfreshSlaveInfos(&rdomains
);
497 DNSSECKeeper
dk(B
); // NOW HEAR THIS! This DK uses our B backend, so no interleaved access!
500 domains_by_name_t
& nameindex
=boost::multi_index::get
<IDTag
>(d_suckdomains
);
502 BOOST_FOREACH(DomainInfo
& di
, rdomains
) {
505 if(di
.masters
.empty()) // slave domains w/o masters are ignored
507 // remove unfresh domains already queued for AXFR, no sense polling them again
508 sr
.master
=*di
.masters
.begin();
509 if(nameindex
.count(sr
)) {
512 DomainNotificationInfo dni
;
514 dni
.dnssecOk
= dk
.isPresigned(di
.zone
);
516 if(dk
.getTSIGForAccess(di
.zone
, sr
.master
, &dni
.tsigkeyname
)) {
518 B
->getTSIGKey(dni
.tsigkeyname
, &dni
.tsigalgname
, &secret64
);
519 B64Decode(secret64
, dni
.tsigsecret
);
521 sdomains
.push_back(dni
);
527 if(d_slaveschanged
) {
529 L
<<Logger::Warning
<<"No new unfresh slave domains, "<<d_suckdomains
.size()<<" queued for AXFR already"<<endl
;
531 d_slaveschanged
= !rdomains
.empty();
536 L
<<Logger::Warning
<<sdomains
.size()<<" slave domain"<<(sdomains
.size()>1 ? "s" : "")<<" need"<<
537 (sdomains
.size()>1 ? "" : "s")<<
538 " checking, "<<d_suckdomains
.size()<<" queued for AXFR"<<endl
;
541 SlaveSenderReceiver ssr
;
543 Inflighter
<vector
<DomainNotificationInfo
>, SlaveSenderReceiver
> ifl(sdomains
, ssr
);
545 ifl
.d_maxInFlight
= 200;
552 catch(std::exception
& e
) {
553 L
<<Logger::Error
<<"While checking domain freshness: " << e
.what()<<endl
;
555 catch(PDNSException
&re
) {
556 L
<<Logger::Error
<<"While checking domain freshness: " << re
.reason
<<endl
;
559 L
<<Logger::Warning
<<"Received serial number updates for "<<ssr
.d_freshness
.size()<<" zones, had "<<ifl
.getTimeouts()<<" timeouts"<<endl
;
561 typedef DomainNotificationInfo val_t
;
562 BOOST_FOREACH(val_t
& val
, sdomains
) {
563 DomainInfo
& di(val
.di
);
564 // might've come from the packethandler
565 if(!di
.backend
&& !B
->getDomainInfo(di
.zone
, di
)) {
566 L
<<Logger::Warning
<<"Ignore domain "<< di
.zone
<<" since it has been removed from our backend"<<endl
;
570 if(!ssr
.d_freshness
.count(di
.id
))
572 uint32_t theirserial
= ssr
.d_freshness
[di
.id
].theirSerial
, ourserial
= di
.serial
;
574 if(rfc1982LessThan(theirserial
, ourserial
)) {
575 L
<<Logger::Error
<<"Domain '"<<di
.zone
<<"' more recent than master, our serial " << ourserial
<< " > their serial "<< theirserial
<< endl
;
576 di
.backend
->setFresh(di
.id
);
578 else if(theirserial
== ourserial
) {
579 if(!dk
.isPresigned(di
.zone
)) {
580 L
<<Logger::Warning
<<"Domain '"<< di
.zone
<<"' is fresh (not presigned, no RRSIG check)"<<endl
;
581 di
.backend
->setFresh(di
.id
);
584 B
->lookup(QType(QType::RRSIG
), di
.zone
); // can't use DK before we are done with this lookup!
585 DNSResourceRecord rr
;
586 uint32_t maxExpire
=0, maxInception
=0;
588 RRSIGRecordContent
rrc(rr
.content
);
589 if(rrc
.d_type
== QType::SOA
) {
590 maxInception
= std::max(maxInception
, rrc
.d_siginception
);
591 maxExpire
= std::max(maxExpire
, rrc
.d_sigexpire
);
594 if(maxInception
== ssr
.d_freshness
[di
.id
].theirInception
&& maxExpire
== ssr
.d_freshness
[di
.id
].theirExpire
) {
595 L
<<Logger::Warning
<<"Domain '"<< di
.zone
<<"' is fresh and apex RRSIGs match"<<endl
;
596 di
.backend
->setFresh(di
.id
);
599 L
<<Logger::Warning
<<"Domain '"<< di
.zone
<<"' is fresh, but RRSIGS differ, so DNSSEC stale"<<endl
;
600 addSuckRequest(di
.zone
, *di
.masters
.begin());
605 L
<<Logger::Warning
<<"Domain '"<< di
.zone
<<"' is stale, master serial "<<theirserial
<<", our serial "<< ourserial
<<endl
;
606 addSuckRequest(di
.zone
, *di
.masters
.begin());
611 // stub for PowerDNSLua linking
612 int directResolve(const std::string
& qname
, const QType
& qtype
, int qclass
, vector
<DNSResourceRecord
>& ret
)