/*
PowerDNS Versatile Database Driven Nameserver
-- Copyright (C) 2003 - 2014 PowerDNS.COM BV
++ Copyright (C) 2003 - 2015 PowerDNS.COM BV
This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License version 2 as published
+ it under the terms of the GNU General Public License version 2 as published
by the Free Software Foundation
Additionally, the license of this program contains a special
fclose(fp);
}
- int SyncRes::asyncresolveWrapper(const ComboAddress& ip, const string& domain, int type, bool doTCP, bool sendRDQuery, struct timeval* now, LWResult* res)
+ int SyncRes::asyncresolveWrapper(const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, struct timeval* now, LWResult* res)
{
/* what is your QUEST?
- the goal is to get as many remotes as possible on the highest level of hipness: EDNS PING responders.
+ the goal is to get as many remotes as possible on the highest level of EDNS support
The levels are:
- -1) CONFIRMEDPINGER: Confirmed pinger!
- 0) UNKNOWN Unknown state
- 1) EDNSNOPING: Honors EDNS0 if no PING is included
- 2) EDNSPINGOK: Ignores EDNS0+PING, but does generate EDNS0 response
- 3) EDNSIGNORANT: Ignores EDNS0+PING, gives replies without EDNS0 nor PING
- 4) NOEDNS: Generates FORMERR on EDNS queries
+ 0) UNKNOWN Unknown state
+ 1) EDNS: Honors EDNS0
+ 2) EDNSIGNORANT: Ignores EDNS0, gives replies without EDNS0
+ 3) NOEDNS: Generates FORMERR on EDNS queries
Everybody starts out assumed to be '0'.
- If '-1', send out EDNS0+Ping
- If we get a FormErr, ignore
- If we get a incorrect PING, ignore
- If we get no PING, ignore
- If '0', send out EDNS0+Ping
- If we get a pure EDNS response, you are downgraded to '2'.
- If you FORMERR us, go to '1',
- If no EDNS in response, go to '3' - 3 and 0 are really identical, except confirmed
- If with correct PING, upgrade to -1
- If '1', send out EDNS0, no PING
- If FORMERR, downgrade to 4
- If '2', keep on including EDNS0+PING, just don't expect PING to be correct
- If PING correct, move to '0', and cheer in the log file!
- If '3', keep on including EDNS0+PING, see what happens
- Same behaviour as 0
- If '4', send bare queries
+ If '0', send out EDNS0
+ If you FORMERR us, go to '3',
+ If no EDNS in response, go to '2'
+ If '1', send out EDNS0
+ If FORMERR, downgrade to 3
+ If '2', keep on including EDNS0, see what happens
+ Same behaviour as 0
+ If '3', send bare queries
*/
- if(s_noEDNS) {
- g_stats.noEdnsOutQueries++;
- return asyncresolve(ip, domain, type, doTCP, sendRDQuery, 0, now, res);
- }
-
+ g_stats.noEdnsOutQueries++;
+
SyncRes::EDNSStatus* ednsstatus;
- ednsstatus = &t_sstorage->ednsstatus[ip];
+ ednsstatus = &t_sstorage->ednsstatus[ip]; // does this include port?
if(ednsstatus->modeSetAt && ednsstatus->modeSetAt + 3600 < d_now.tv_sec) {
*ednsstatus=SyncRes::EDNSStatus();
}
else {
const ComboAddress remoteIP = servers.front();
- LOG(prefix<<qname<<": forwarding query to hardcoded nameserver '"<< remoteIP.toStringWithPort()<<"' for zone '"<<authname<<"'"<<endl);
+ LOG(prefix<<qname.toString()<<": forwarding query to hardcoded nameserver '"<< remoteIP.toStringWithPort()<<"' for zone '"<<authname.toString()<<"'"<<endl);
- res=asyncresolveWrapper(remoteIP, qname, qtype.getCode(), false, false, &d_now, &lwr);
+ res=asyncresolveWrapper(remoteIP, qname, qtype.getCode(), false, false, &d_now, &lwr);
// filter out the good stuff from lwr.result()
- for(LWResult::res_t::const_iterator i=lwr.d_result.begin();i!=lwr.d_result.end();++i) {
+ for(LWResult::res_t::const_iterator i=lwr.getResult().begin();i!=lwr.getResult().end();++i) {
if(i->d_place == DNSResourceRecord::ANSWER)
ret.push_back(*i);
}
}
set<DNSResourceRecord> cset;
bool found=false, expired=false;
-
- if(t_RC->get(d_now.tv_sec, sqname, sqt, &cset) > 0) {
+ vector<std::shared_ptr<RRSIGRecordContent>> signatures;
+ uint32_t ttl=0;
+ if(t_RC->get(d_now.tv_sec, sqname, sqt, &cset, d_doDNSSEC ? &signatures : 0) > 0) {
- LOG(prefix<<sqname<<": Found cache hit for "<<sqt.getName()<<": ");
+ LOG(prefix<<sqname.toString()<<": Found cache hit for "<<sqt.getName()<<": ");
for(set<DNSResourceRecord>::const_iterator j=cset.begin();j!=cset.end();++j) {
LOG(j->content);
if(j->ttl>(unsigned int) d_now.tv_sec) {
if(lwr.d_tcbit) {
if(!doTCP) {
doTCP=true;
- LOG(prefix<<qname<<": truncated bit set, retrying via TCP"<<endl);
+ LOG(prefix<<qname.toString()<<": truncated bit set, retrying via TCP"<<endl);
goto TryTCP;
}
- LOG(prefix<<qname<<": truncated bit set, over TCP?"<<endl);
+ LOG(prefix<<qname.toString()<<": truncated bit set, over TCP?"<<endl);
return RCode::ServFail;
}
-
- LOG(prefix<<qname<<": Got "<<(unsigned int)lwr.getResult().size()<<" answers from "<<*tns<<" ("<< remoteIP->toString() <<"), rcode="<<lwr.d_rcode<<" ("<<RCode::to_s(lwr.d_rcode)<<"), aa="<<lwr.d_aabit<<", in "<<lwr.d_usec/1000<<"ms"<<endl);
-
+ LOG(prefix<<qname.toString()<<": Got "<<(unsigned int)lwr.d_result.size()<<" answers from "<<tns->toString()<<" ("<< remoteIP->toString() <<"), rcode="<<lwr.d_rcode<<" ("<<RCode::to_s(lwr.d_rcode)<<"), aa="<<lwr.d_aabit<<", in "<<lwr.d_usec/1000<<"ms"<<endl);
/* // for you IPv6 fanatics :-)
if(remoteIP->sin4.sin_family==AF_INET6)
}
}
- typedef map<pair<DNSName, QType>, set<DNSResourceRecord>, TCacheComp > tcache_t;
+ struct CachePair
+ {
+ set<DNSResourceRecord> records;
+ vector<shared_ptr<RRSIGRecordContent>> signatures;
+ };
- typedef map<pair<string, QType>, CachePair, TCacheComp > tcache_t;
++ typedef map<pair<DNSName, QType>, CachePair, TCacheComp > tcache_t;
tcache_t tcache;
- cerr<<"Got an RRSIG for "<<DNSRecordContent::NumberToType(rrsig->d_type)<<" with name '"<<stripDot(rec.first.d_label)<<"'"<<endl;
+ if(d_doDNSSEC) {
+ for(const auto& rec : lwr.d_records) {
+ if(rec.first.d_type == QType::RRSIG) {
+ auto rrsig = std::dynamic_pointer_cast<RRSIGRecordContent>(rec.first.d_content);
++ cerr<<"Got an RRSIG for "<<DNSRecordContent::NumberToType(rrsig->d_type)<<" with name '"<<rec.first.d_label.toString()<<"'"<<endl;
+ tcache[make_pair(rec.first.d_label, QType(rrsig->d_type))].signatures.push_back(rrsig);
+ }
+ }
+ }
+
// reap all answers from this packet that are acceptable
- for(LWResult::res_t::iterator i=lwr.d_result.begin();i != lwr.d_result.end();++i) {
+ for(LWResult::res_t::iterator i=lwr.getResult().begin();i != lwr.getResult().end();++i) {
if(i->qtype.getCode() == QType::OPT) {
- LOG(prefix<<qname<<": skipping OPT answer '"<<i->qname<<"' from '"<<auth<<"' nameservers" <<endl);
+ LOG(prefix<<qname.toString()<<": skipping OPT answer '"<<i->qname.toString()<<"' from '"<<auth.toString()<<"' nameservers" <<endl);
continue;
}
- LOG(prefix<<qname<<": accept answer '"<<i->qname<<"|"<<i->qtype.getName()<<"|"<<i->content<<"' from '"<<auth<<"' nameservers? ");
+ LOG(prefix<<qname.toString()<<": accept answer '"<<i->qname.toString()<<"|"<<i->qtype.getName()<<"|"<<i->content<<"' from '"<<auth.toString()<<"' nameservers? ");
if(i->qtype.getCode()==QType::ANY) {
LOG("NO! - we don't accept 'ANY' data"<<endl);
continue;
if(rr.qtype.getCode() == QType::NS) // people fiddle with the case
rr.content=toLower(rr.content); // this must stay! (the cache can't be case-insensitive on the RHS of records)
-
-
- tcache[make_pair(i->qname,i->qtype)].insert(rr);
+ tcache[make_pair(i->qname,i->qtype)].records.insert(rr);
}
- }
+ }
else
LOG("NO!"<<endl);
}
-
+
// supplant
for(tcache_t::iterator i=tcache.begin();i!=tcache.end();++i) {
- if(i->second.size() > 1) { // need to group the ttl to be the minimum of the RRSET (RFC 2181, 5.2)
+ if(i->second.records.size() > 1) { // need to group the ttl to be the minimum of the RRSET (RFC 2181, 5.2)
uint32_t lowestTTL=std::numeric_limits<uint32_t>::max();
- for(tcache_t::value_type::second_type::iterator j=i->second.begin(); j != i->second.end(); ++j)
- lowestTTL=min(lowestTTL, j->ttl);
-
- for(tcache_t::value_type::second_type::iterator j=i->second.begin(); j != i->second.end(); ++j)
- ((tcache_t::value_type::second_type::value_type*)&(*j))->ttl=lowestTTL;
+ for(auto& record : i->second.records)
+ lowestTTL=min(lowestTTL, record.ttl);
+
+ for(auto& record : i->second.records)
+ *const_cast<uint32_t*>(&record.ttl)=lowestTTL;
}
- cout<<"Have "<<i->second.records.size()<<" records and "<<i->second.signatures.size()<<" signatures for "<<i->first.first;
- t_RC->replace(d_now.tv_sec, i->first.first, i->first.second, i->second, lwr.d_aabit);
++ cout<<"Have "<<i->second.records.size()<<" records and "<<i->second.signatures.size()<<" signatures for "<<i->first.first.toString();
+ cout<<'|'<<DNSRecordContent::NumberToType(i->first.second.getCode())<<endl;
+ t_RC->replace(d_now.tv_sec, i->first.first, i->first.second, i->second.records, i->second.signatures, lwr.d_aabit);
}
- set<string, CIStringCompare> nsset;
- LOG(prefix<<qname<<": determining status after receiving this packet"<<endl);
+ set<DNSName> nsset;
+ LOG(prefix<<qname.toString()<<": determining status after receiving this packet"<<endl);
- bool done=false, realreferral=false, negindic=false;
- DNSName newauth, newtarget;
+ bool done=false, realreferral=false, negindic=false, sawDS=false;
- string newauth, soaname, newtarget;
-
++ DNSName newauth, soaname;
++ DNSName newtarget;
- for(LWResult::res_t::iterator i=lwr.d_result.begin();i!=lwr.d_result.end();++i) {
- if(i->d_place==DNSResourceRecord::AUTHORITY && i->qtype.getCode()==QType::SOA &&
+ for(LWResult::res_t::iterator i=lwr.getResult().begin();i!=lwr.getResult().end();++i) {
+ if(i->d_place==DNSResourceRecord::AUTHORITY && i->qtype.getCode()==QType::SOA &&
lwr.d_rcode==RCode::NXDomain && dottedEndsOn(qname,i->qname) && dottedEndsOn(i->qname, auth)) {
- LOG(prefix<<qname<<": got negative caching indication for RECORD '"<<qname+"' (accept="<<dottedEndsOn(i->qname, auth)<<"), newtarget='"<<newtarget<<"'"<<endl);
-
+ LOG(prefix<<qname.toString()<<": got negative caching indication for name '"<<qname.toString()+"' (accept="<<dottedEndsOn(i->qname, auth)<<"), newtarget='"<<newtarget.toString()<<"'"<<endl);
+
i->ttl = min(i->ttl, s_maxnegttl);
if(!newtarget.length()) // only add a SOA if we're not going anywhere after this
ret.push_back(*i);
ret.push_back(*i);
newtarget=i->content;
}
+ else if(d_doDNSSEC && (i->qtype==QType::RRSIG || i->qtype==QType::NSEC || i->qtype==QType::NSEC3) && i->d_place==DNSResourceRecord::ANSWER){
+ ret.push_back(*i); // enjoy your DNSSEC
+ }
// for ANY answers we *must* have an authoritative answer, unless we are forwarding recursively
- else if(i->d_place==DNSResourceRecord::ANSWER && pdns_iequals(i->qname, qname) &&
+ else if(i->d_place==DNSResourceRecord::ANSWER && pdns_iequals(i->qname, qname) &&
(
i->qtype==qtype || (lwr.d_aabit && (qtype==QType(QType::ANY) || magicAddrMatch(qtype, i->qtype) ) ) || sendRDQuery
- )
- )
+ )
+ )
{
-
- LOG(prefix<<qname<<": answer is in: resolved to '"<< i->content<<"|"<<i->qtype.getName()<<"'"<<endl);
+
+ LOG(prefix<<qname.toString()<<": answer is in: resolved to '"<< i->content<<"|"<<i->qtype.getName()<<"'"<<endl);
done=true;
ret.push_back(*i);
}
- else if(i->d_place==DNSResourceRecord::AUTHORITY && dottedEndsOn(qname,i->qname) && i->qtype.getCode()==QType::NS) {
+ else if(i->d_place==DNSResourceRecord::AUTHORITY && qname.isPartOf(i->qname) && i->qtype.getCode()==QType::NS) {
if(moreSpecificThan(i->qname,auth)) {
newauth=i->qname;
- LOG(prefix<<qname<<": got NS record '"<<i->qname<<"' -> '"<<i->content<<"'"<<endl);
+ LOG(prefix<<qname.toString()<<": got NS record '"<<i->qname.toString()<<"' -> '"<<i->content<<"'"<<endl);
realreferral=true;
}
- else
- LOG(prefix<<qname<<": got upwards/level NS record '"<<i->qname<<"' -> '"<<i->content<<"', had '"<<auth<<"'"<<endl);
+ else
+ LOG(prefix<<qname.toString()<<": got upwards/level NS record '"<<i->qname.toString()<<"' -> '"<<i->content<<"', had '"<<auth.toString()<<"'"<<endl);
nsset.insert(i->content);
}
- else if(!done && i->d_place==DNSResourceRecord::AUTHORITY && dottedEndsOn(qname,i->qname) && i->qtype.getCode()==QType::SOA &&
+ else if(i->d_place==DNSResourceRecord::AUTHORITY && dottedEndsOn(qname,i->qname) && i->qtype.getCode()==QType::DS) {
- LOG(prefix<<qname<<": got DS record '"<<i->qname<<"' -> '"<<i->content<<"'"<<endl);
++ LOG(prefix<<qname.toString()<<": got DS record '"<<i->qname.toString()<<"' -> '"<<i->content<<"'"<<endl);
+ sawDS=true;
+ }
+ else if(!done && i->d_place==DNSResourceRecord::AUTHORITY && dottedEndsOn(qname,i->qname) && i->qtype.getCode()==QType::SOA &&
lwr.d_rcode==RCode::NoError) {
- LOG(prefix<<qname<<": got negative caching indication for '"<< (qname+"|"+qtype.getName()+"'") <<endl);
-
+ LOG(prefix<<qname.toString()<<": got negative caching indication for '"<< (qname.toString()+"|"+qtype.getName()+"'") <<endl);
+
if(!newtarget.empty()) {
- LOG(prefix<<qname<<": Hang on! Got a redirect to '"<<newtarget<<"' already"<<endl);
+ LOG(prefix<<qname.toString()<<": Hang on! Got a redirect to '"<<newtarget.toString()<<"' already"<<endl);
}
else {
i-> ttl = min(s_maxnegttl, i->ttl);
return 0;
}
else if(realreferral) {
- LOG(prefix<<qname<<": status=did not resolve, got "<<(unsigned int)nsset.size()<<" NS, looping to them"<<endl);
+ LOG(prefix<<qname.toString()<<": status=did not resolve, got "<<(unsigned int)nsset.size()<<" NS, looping to them"<<endl);
+ if(sawDS) {
+ t_sstorage->dnssecmap[newauth]=true;
+ for(const auto& e : t_sstorage->dnssecmap)
- cout<<e.first<<' ';
++ cout<<e.first.toString()<<' ';
+ cout<<endl;
+ }
auth=newauth;
nameservers=nsset;
- break;
+ break;
}
else if(isCanonical(*tns)) { // means: not OOB (I think)
goto wasLame;