void CommunicatorClass::suck(const string &domain,const string &remote)
{
L<<Logger::Error<<"Initiating transfer of '"<<domain<<"' from remote '"<<remote<<"'"<<endl;
- uint32_t domain_id;
PacketHandler P; // fresh UeberBackend
DomainInfo di;
di.backend=0;
- bool first=true;
+ bool transaction=false;
try {
UeberBackend *B=dynamic_cast<UeberBackend *>(P.getBackend()); // copy of the same UeberBackend
- NSEC3PARAMRecordContent ns3pr, hadNs3pr;
- bool narrow, hadNarrow=false;
DNSSECKeeper dk (B); // reuse our backend for DNSSECKeeper
- bool dnssecZone = false;
- bool haveNSEC3=false;
- if(dk.isSecuredZone(domain)) {
- dnssecZone=true;
- haveNSEC3=dk.getNSEC3PARAM(domain, &ns3pr, &narrow);
- if (haveNSEC3) {
- hadNs3pr = ns3pr;
- hadNarrow = narrow;
- }
- }
-
- const bool hadNSEC3 = haveNSEC3;
- const bool hadPresigned = dk.isPresigned(domain);
- const bool hadDnssecZone = dnssecZone;
if(!B->getDomainInfo(domain, di) || !di.backend) { // di.backend and B are mostly identical
L<<Logger::Error<<"Can't determine backend for domain '"<<domain<<"'"<<endl;
return;
}
- domain_id=di.id;
+ uint32_t domain_id=di.id;
- Resolver::res_t recs;
- set<string> nsset, qnames;
-
- ComboAddress raddr(remote, 53);
-
string tsigkeyname, tsigalgorithm, tsigsecret;
if(dk.getTSIGForAccess(domain, remote, &tsigkeyname)) {
vector<string> localaddr;
ComboAddress laddr;
-
+
if(B->getDomainMetadata(domain, "AXFR-SOURCE", localaddr) && !localaddr.empty()) {
try {
laddr = ComboAddress(localaddr[0]);
return;
}
} else {
- laddr.sin4.sin_family = 0;
+ laddr.sin4.sin_family = 0;
+ }
+
+ bool hadDnssecZone = false;
+ bool hadPresigned = false;
+ bool hadNSEC3 = false;
+ NSEC3PARAMRecordContent ns3pr, hadNs3pr;
+ bool isNarrow, hadNarrow=false;
+
+ if(dk.isSecuredZone(domain)) {
+ hadDnssecZone=true;
+ hadPresigned=dk.isPresigned(domain);
+ if (dk.getNSEC3PARAM(domain, &ns3pr, &isNarrow)) {
+ hadNSEC3 = true;
+ hadNs3pr = ns3pr;
+ hadNarrow = isNarrow;
+ }
}
- AXFRRetriever retriever(raddr, domain.c_str(), tsigkeyname, tsigalgorithm, tsigsecret,
- (laddr.sin4.sin_family == 0) ? NULL : &laddr);
+ bool isDnssecZone = false;
+ bool isPresigned = false;
+ bool isNSEC3 = false;
+ bool optOutFlag = false;
- bool gotPresigned = false;
- bool gotNSEC3 = false;
- bool gotOptOutFlag = false;
+ bool first=true;
+ bool firstNSEC3=true;
unsigned int soa_serial = 0;
+ set<string> nsset, qnames, secured;
vector<DNSResourceRecord> rrs;
- set<string> secured;
+
+ ComboAddress raddr(remote, 53);
+ AXFRRetriever retriever(raddr, domain.c_str(), tsigkeyname, tsigalgorithm, tsigsecret, (laddr.sin4.sin_family == 0) ? NULL : &laddr);
+ Resolver::res_t recs;
while(retriever.getChunk(recs)) {
if(first) {
L<<Logger::Error<<"AXFR started for '"<<domain<<"'"<<endl;
if(i->qtype.getCode() == QType::OPT || i->qtype.getCode() == QType::TSIG) // ignore EDNS0 & TSIG
continue;
- if(!endsOn(i->qname, domain)) {
+ if(!endsOn(i->qname, domain)) {
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;
continue;
}
- if (i->qtype.getCode() == QType::NSEC3PARAM) {
- ns3pr = NSEC3PARAMRecordContent(i->content);
- narrow = false;
- dnssecZone = haveNSEC3 = gotPresigned = gotNSEC3 = true;
- continue;
- } else if (i->qtype.getCode() == QType::NSEC3) {
- dnssecZone = gotPresigned = true;
- NSEC3RecordContent ns3rc(i->content);
- gotOptOutFlag = ns3rc.d_flags & 1;
- if (ns3rc.d_set.count(QType::NS) && !pdns_iequals(i->qname, domain))
- secured.insert(toLower(makeRelative(i->qname, domain)));
- continue;
- } else if (i->qtype.getCode() == QType::NSEC) {
- dnssecZone = gotPresigned = true;
- continue;
- }
-
- if(i->qtype.getCode() == QType::SOA) {
- if(soa_serial != 0)
- continue; //skip the last SOA
- SOAData sd;
- fillSOAData(i->content,sd);
- soa_serial = sd.serial;
+ vector<DNSResourceRecord> out;
+ if(!pdl || !pdl->axfrfilter(raddr, domain, *i, out)) {
+ out.push_back(*i);
}
- i->domain_id=domain_id;
+ BOOST_FOREACH(DNSResourceRecord& rr, out) {
+ switch(rr.qtype.getCode()) {
+ case QType::NSEC3PARAM: {
+ ns3pr = NSEC3PARAMRecordContent(rr.content);
+ isDnssecZone = isNSEC3 = true;
+ isNarrow = false;
+ continue;
+ }
+ case QType::NSEC3: {
+ NSEC3RecordContent ns3rc(rr.content);
+ if (firstNSEC3) {
+ isDnssecZone = isPresigned = true;
+ firstNSEC3 = false;
+ } else if (optOutFlag != (ns3rc.d_flags & 1))
+ throw AhuException("Zones with a mixture of Opt-Out NSEC3 RRs and non-Opt-Out NSEC3 RRs are not supported.");
+ optOutFlag = ns3rc.d_flags & 1;
+ if (ns3rc.d_set.count(QType::NS) && !pdns_iequals(rr.qname, domain))
+ secured.insert(toLower(makeRelative(rr.qname, domain)));
+ continue;
+ }
+ case QType::NSEC: {
+ isDnssecZone = isPresigned = true;
+ continue;
+ }
+ case QType::SOA: {
+ if(soa_serial != 0)
+ continue; //skip the last SOA
+ SOAData sd;
+ fillSOAData(rr.content,sd);
+ soa_serial = sd.serial;
+ break;
+ }
+ case QType::NS: {
+ if(!pdns_iequals(rr.qname, domain))
+ nsset.insert(rr.qname);
+ break;
+ }
+ default:
+ break;
+ }
-#if 0
- if(i->qtype.getCode()>=60000)
- throw DBException("Database can't store unknown record type "+lexical_cast<string>(i->qtype.getCode()-1024));
-#endif
+ qnames.insert(rr.qname);
- vector<DNSResourceRecord> out;
- if(pdl && pdl->axfrfilter(raddr, domain, *i, out)) {
- BOOST_FOREACH(const DNSResourceRecord& rr, out) {
- rrs.push_back(rr);
- }
- } else {
- rrs.push_back(*i);
+ rr.domain_id=domain_id;
+ rrs.push_back(rr);
}
}
}
+ if(isNSEC3) {
+ ns3pr.d_flags = optOutFlag ? 1 : 0;
+ }
+
- BOOST_FOREACH(const DNSResourceRecord& rr, rrs) {
- if(rr.qtype.getCode() == QType::NS && !pdns_iequals(rr.qname, domain))
- nsset.insert(rr.qname);
- qnames.insert(rr.qname);
+ if(!isPresigned) {
+ DNSSECKeeper::keyset_t keys = dk.getKeys(domain);
+ if(!keys.empty()) {
+ isDnssecZone = true;
+ isNSEC3 = hadNSEC3;
+ ns3pr = hadNs3pr;
+ optOutFlag = (hadNs3pr.d_flags & 1);
+ isNarrow = hadNarrow;
+ }
}
- if(dnssecZone) {
- if(!haveNSEC3)
+
+ if(isDnssecZone) {
+ if(!isNSEC3)
L<<Logger::Info<<"Adding NSEC ordering information"<<endl;
- else if(!narrow)
+ else if(!isNarrow)
L<<Logger::Info<<"Adding NSEC3 hashed ordering information for '"<<domain<<"'"<<endl;
else
L<<Logger::Info<<"Erasing NSEC3 ordering since we are narrow, only setting 'auth' fields"<<endl;
}
- if (hadPresigned && !gotNSEC3) { // not sure why this is here
- // we only had NSEC3 because we were a presigned zone...
- haveNSEC3 = false;
- }
-
+ transaction=di.backend->startTransaction(domain, domain_id);
L<<Logger::Error<<"Transaction started for '"<<domain<<"'"<<endl;
- di.backend->startTransaction(domain, domain_id);
+
+ // update the presigned flag and NSEC3PARAM
+ if (isDnssecZone) {
+ // update presigned if there was a change
+ if (isPresigned && !hadPresigned) {
+ // zone is now presigned
+ dk.setPresigned(domain);
+ } else if (hadPresigned && !isPresigned) {
+ // zone is no longer presigned
+ dk.unsetPresigned(domain);
+ }
+ // update NSEC3PARAM
+ if (isNSEC3) {
+ // zone is NSEC3, only update if there was a change
+ if (!hadNSEC3 || (hadNarrow != isNarrow) ||
+ (ns3pr.d_algorithm != hadNs3pr.d_algorithm) ||
+ (ns3pr.d_flags != hadNs3pr.d_flags) ||
+ (ns3pr.d_iterations != hadNs3pr.d_iterations) ||
+ (ns3pr.d_salt != hadNs3pr.d_salt)) {
+ dk.setNSEC3PARAM(domain, ns3pr, isNarrow);
+ }
+ } else if (hadNSEC3 ) {
+ // zone is no longer NSEC3
+ dk.unsetNSEC3PARAM(domain);
+ }
+ } else if (hadDnssecZone) {
+ // zone is no longer signed
+ if (hadPresigned) {
+ // remove presigned
+ dk.unsetPresigned(domain);
+ }
+ if (hadNSEC3) {
+ // unset NSEC3PARAM
+ dk.unsetNSEC3PARAM(domain);
+ }
+ }
bool doent=true;
uint32_t maxent = ::arg().asNum("max-ent-entries");
string ordername, shorter;
- set<string> nonterm, rrterm;
+ set<string> rrterm, nonterm;
+
BOOST_FOREACH(DNSResourceRecord& rr, rrs) {
+ if(!isPresigned) {
+ if (rr.qtype.getCode() == QType::RRSIG)
+ continue;
+ if(isDnssecZone && rr.qtype.getCode() == QType::DNSKEY && !::arg().mustDo("direct-dnskey"))
+ continue;
+ }
+
// Figure out auth and ents
rr.auth=true;
shorter=rr.qname;
rrterm.clear();
do {
if(doent) {
- if (!qnames.count(shorter) && !nonterm.count(shorter) && !rrterm.count(shorter))
+ if (!qnames.count(shorter))
rrterm.insert(shorter);
}
- if(nsset.count(shorter) && rr.qtype.getCode() != QType::DS) {
+ if(nsset.count(shorter) && rr.qtype.getCode() != QType::DS)
rr.auth=false;
- break;
- }
+
if (pdns_iequals(shorter, domain)) // stop at apex
break;
}while(chopOff(shorter));
- // Insert ents for auth rrs
- if(doent && rr.auth) {
- nonterm.insert(rrterm.begin(), rrterm.end());
+ // Insert ents
+ if(doent && !rrterm.empty()) {
+ //bool auth;
+ if (!rr.auth && rr.qtype.getCode() == QType::NS) {
+ if (isNSEC3)
+ ordername=toLower(toBase32Hex(hashQNameWithSalt(ns3pr.d_iterations, ns3pr.d_salt, rr.qname)));
+ //auth=(!isNSEC3 || !optOutFlag || secured.count(ordername));
+ } //else
+ //auth=rr.auth;
+
+ BOOST_FOREACH(const string nt, rrterm){
+ if (!nonterm.count(nt))
+ nonterm.insert(nt);
+ }
+
if(nonterm.size() > maxent) {
- L<<Logger::Error<<"AXFR zone "<<domain<<" has too many empty non terminals."<<endl;
+ if (!::arg().asNum("max-ent-entries"))
+ L<<Logger::Error<<"AXFR zone "<<domain<<" has too many empty non terminals."<<endl;
nonterm.clear();
doent=false;
}
rr.auth=true;
// Add ordername and insert record
- if (dnssecZone && rr.qtype.getCode() != QType::RRSIG) {
- if (haveNSEC3) {
+ if (isDnssecZone && rr.qtype.getCode() != QType::RRSIG) {
+ if (isNSEC3) {
// NSEC3
ordername=toLower(toBase32Hex(hashQNameWithSalt(ns3pr.d_iterations, ns3pr.d_salt, rr.qname)));
- if(!narrow && (rr.auth || (rr.qtype.getCode() == QType::NS && (!gotOptOutFlag || secured.count(ordername))))) {
+ if(!isNarrow && (rr.auth || (rr.qtype.getCode() == QType::NS && (!optOutFlag || secured.count(ordername))))) {
di.backend->feedRecord(rr, &ordername);
} else
di.backend->feedRecord(rr);
// Insert empty non-terminals
if(doent && !nonterm.empty()) {
- if (haveNSEC3) {
- di.backend->feedEnts3(domain_id, domain, nonterm, ns3pr.d_iterations, ns3pr.d_salt, narrow);
+ if (isNSEC3) {
+ di.backend->feedEnts3(domain_id, domain, nonterm, ns3pr.d_iterations, ns3pr.d_salt, isNarrow);
} else
di.backend->feedEnts(domain_id, nonterm);
}
- // now we also need to update the presigned flag and NSEC3PARAM
- // for the zone
- if (gotPresigned) {
- if (!hadDnssecZone && !hadPresigned) {
- // zone is now presigned
- dk.setPresigned(domain);
- }
-
- if (hadPresigned || !hadDnssecZone)
- {
- // this is a presigned zone, update NSEC3PARAM
- if (gotNSEC3) {
- ns3pr.d_flags = gotOptOutFlag ? 1 : 0;
- // only update if there was a change
- if (!hadNSEC3 || (narrow != hadNarrow) ||
- (ns3pr.d_algorithm != hadNs3pr.d_algorithm) ||
- (ns3pr.d_flags != hadNs3pr.d_flags) ||
- (ns3pr.d_iterations != hadNs3pr.d_iterations) ||
- (ns3pr.d_salt != hadNs3pr.d_salt)) {
- dk.setNSEC3PARAM(domain, ns3pr, narrow);
- }
- } else if (hadNSEC3) {
- dk.unsetNSEC3PARAM(domain);
- }
- }
- } else if (hadPresigned) {
- // zone is no longer presigned
- dk.unsetPresigned(domain);
- dk.unsetNSEC3PARAM(domain);
- }
-
di.backend->commitTransaction();
+ transaction = false;
di.backend->setFresh(domain_id);
PC.purge(domain+"$");
}
catch(DBException &re) {
L<<Logger::Error<<"Unable to feed record during incoming AXFR of '"+domain+"': "<<re.reason<<endl;
- if(di.backend && !first) {
+ if(di.backend && transaction) {
L<<Logger::Error<<"Aborting possible open transaction for domain '"<<domain<<"' AXFR"<<endl;
di.backend->abortTransaction();
}
}
catch(MOADNSException &re) {
L<<Logger::Error<<"Unable to parse record during incoming AXFR of '"+domain+"' (MOADNSException): "<<re.what()<<endl;
- if(di.backend && !first) {
+ if(di.backend && transaction) {
L<<Logger::Error<<"Aborting possible open transaction for domain '"<<domain<<"' AXFR"<<endl;
di.backend->abortTransaction();
}
}
catch(std::exception &re) {
L<<Logger::Error<<"Unable to parse record during incoming AXFR of '"+domain+"' (std::exception): "<<re.what()<<endl;
- if(di.backend && !first) {
+ if(di.backend && transaction) {
L<<Logger::Error<<"Aborting possible open transaction for domain '"<<domain<<"' AXFR"<<endl;
di.backend->abortTransaction();
}
}
catch(ResolverException &re) {
L<<Logger::Error<<"Unable to AXFR zone '"+domain+"' from remote '"<<remote<<"' (resolver): "<<re.reason<<endl;
- if(di.backend && !first) {
+ if(di.backend && transaction) {
L<<Logger::Error<<"Aborting possible open transaction for domain '"<<domain<<"' AXFR"<<endl;
di.backend->abortTransaction();
}
}
catch(AhuException &ae) {
L<<Logger::Error<<"Unable to AXFR zone '"+domain+"' from remote '"<<remote<<"' (AhuException): "<<ae.reason<<endl;
- if(di.backend && !first) {
+ if(di.backend && transaction) {
L<<Logger::Error<<"Aborting possible open transaction for domain '"<<domain<<"' AXFR"<<endl;
di.backend->abortTransaction();
}