From 57769f13b5be0f47d1bf3d588cdf0db73df3da51 Mon Sep 17 00:00:00 2001 From: bert hubert Date: Thu, 28 May 2015 23:21:11 +0200 Subject: [PATCH] strip EDNS ping support, add detection of CD and DNSSEC-OK queries, harvest signatures, store signatures in cache, add them when required, prepare LWRes for day when we no longer store and process records in 'human readable form' --- pdns/lwres.cc | 47 ++++----- pdns/lwres.hh | 7 +- pdns/pdns_recursor.cc | 11 +-- pdns/recursor_cache.cc | 8 +- pdns/recursor_cache.hh | 9 +- pdns/reczones.cc | 10 +- pdns/syncres.cc | 212 ++++++++++++++++++++--------------------- pdns/syncres.hh | 9 +- 8 files changed, 150 insertions(+), 163 deletions(-) diff --git a/pdns/lwres.cc b/pdns/lwres.cc index fe4eb9dea4..7741cef360 100644 --- a/pdns/lwres.cc +++ b/pdns/lwres.cc @@ -68,18 +68,11 @@ int asyncresolve(const ComboAddress& ip, const string& domain, int type, bool do if(EDNS0Level && !doTCP) { DNSPacketWriter::optvect_t opts; - if(EDNS0Level > 1) { - uint32_t nonce=dns_random(0xffffffff); - ping.assign((char*) &nonce, 4); - opts.push_back(make_pair(5, ping)); - } - - pw.addOpt(1200, 0, 0, opts); // 1200 bytes answer size + pw.addOpt(1200, 0, EDNSOpts::DNSSECOK, opts); // 1200 bytes answer size pw.commit(); } lwr->d_rcode = 0; - lwr->d_pingCorrect = false; lwr->d_haveEDNS = false; int ret; @@ -177,28 +170,12 @@ int asyncresolve(const ComboAddress& ip, const string& domain, int type, bool do goto out; } - for(MOADNSParser::answers_t::const_iterator i=mdp.d_answers.begin(); i!=mdp.d_answers.end(); ++i) { - DNSResourceRecord rr; - rr.qtype=i->first.d_type; - rr.qname=i->first.d_label; - rr.ttl=i->first.d_ttl; - rr.content=i->first.d_content->getZoneRepresentation(); // this should be the serialised form - rr.d_place=(DNSResourceRecord::Place) i->first.d_place; - lwr->d_result.push_back(rr); - } + lwr->d_records = mdp.d_answers; + EDNSOpts edo; - if(EDNS0Level > 1 && getEDNSOpts(mdp, &edo)) { + if(EDNS0Level > 0 && getEDNSOpts(mdp, &edo)) { lwr->d_haveEDNS = true; - for(vector >::const_iterator iter = edo.d_options.begin(); - iter != edo.d_options.end(); - ++iter) { - if(iter->first == 5 || iter->first == 4) {// 'EDNS PING' - if(iter->second == ping) { - lwr->d_pingCorrect = true; - } - } - } } return 1; @@ -223,4 +200,18 @@ int asyncresolve(const ComboAddress& ip, const string& domain, int type, bool do return -1; } - +vector& LWResult::getResult() +{ + if(d_result.empty()) { + for(auto i=d_records.begin(); i != d_records.end(); ++i) { + DNSResourceRecord rr; + rr.qtype=i->first.d_type; + rr.qname=i->first.d_label; + rr.ttl=i->first.d_ttl; + rr.content=i->first.d_content->getZoneRepresentation(); // this should be the serialised form + rr.d_place=(DNSResourceRecord::Place) i->first.d_place; + d_result.push_back(rr); + } + } + return d_result; +} diff --git a/pdns/lwres.hh b/pdns/lwres.hh index 0d43249c8f..23edb17c30 100644 --- a/pdns/lwres.hh +++ b/pdns/lwres.hh @@ -34,6 +34,7 @@ #include #include #include +#include "dnsparser.hh" #include #undef res_mkquery @@ -58,13 +59,15 @@ class LWResult public: LWResult() : d_usec(0) {} typedef vector res_t; + res_t& getResult(); - vector d_result; + vector> d_records; int d_rcode; bool d_aabit, d_tcbit; uint32_t d_usec; - bool d_pingCorrect; bool d_haveEDNS; + + vector d_result; }; int asyncresolve(const ComboAddress& ip, const string& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, LWResult* res); diff --git a/pdns/pdns_recursor.cc b/pdns/pdns_recursor.cc index 7ae2b40801..3bfd0b3290 100644 --- a/pdns/pdns_recursor.cc +++ b/pdns/pdns_recursor.cc @@ -555,6 +555,7 @@ void startDoResolve(void *p) pw.getHeader()->tc=0; pw.getHeader()->id=dc->d_mdp.d_header.id; pw.getHeader()->rd=dc->d_mdp.d_header.rd; + pw.getHeader()->cd=dc->d_mdp.d_header.cd; uint32_t minTTL=std::numeric_limits::max(); @@ -563,6 +564,10 @@ void startDoResolve(void *p) sr.setLuaEngine(*t_pdl); sr.d_requestor=dc->d_remote; } + + if(pw.getHeader()->cd || edo.d_Z & EDNSOpts::DNSSECOK) + sr.d_doDNSSEC=true; + bool tracedQuery=false; // we could consider letting Lua know about this too bool variableAnswer = false; @@ -1987,12 +1992,6 @@ int serviceMain(int argc, char*argv[]) exit(99); } - SyncRes::s_noEDNSPing = true; // ::arg().mustDo("disable-edns-ping"); - SyncRes::s_noEDNS = ::arg().mustDo("disable-edns"); - if(!SyncRes::s_noEDNS) { - L<* res) +int MemRecursorCache::get(time_t now, const string &qname, const QType& qt, set* res, vector>* signatures) { unsigned int ttd=0; // cerr<<"looking up "<< qname+"|"+qt.getName()<<"\n"; @@ -137,6 +137,8 @@ int MemRecursorCache::get(time_t now, const string &qname, const QType& qt, set< } } } + if(signatures) // if you do an ANY lookup you are hosed XXXX + *signatures=i->d_signatures; if(res) { if(res->empty()) moveCacheItemToFront(d_cache, i); @@ -186,7 +188,7 @@ bool MemRecursorCache::attemptToRefreshNSTTL(const QType& qt, const set& content, bool auth) +void MemRecursorCache::replace(time_t now, const string &qname, const QType& qt, const set& content, const vector>& signatures, bool auth) { d_cachecachevalid=false; boost::tuple key=boost::make_tuple(qname, qt.getCode()); @@ -202,7 +204,7 @@ void MemRecursorCache::replace(time_t now, const string &qname, const QType& qt, StoredRecord dr; CacheEntry ce=*stored; - + ce.d_signatures=signatures; //~ cerr<<"asked to store "<< qname+"|"+qt.getName()<<" -> '"<content<<"', isnew="< - +#include "dnsrecords.hh" #include #undef L #include @@ -31,10 +31,10 @@ public: } unsigned int size(); unsigned int bytes(); - int get(time_t, const string &qname, const QType& qt, set* res); + int get(time_t, const string &qname, const QType& qt, set* res, vector>* signatures=0); int getDirect(time_t now, const char* qname, const QType& qt, uint32_t ttd[10], char* data[10], uint16_t len[10]); - void replace(time_t, const string &qname, const QType& qt, const set& content, bool auth); + void replace(time_t, const string &qname, const QType& qt, const set& content, const vector>& signatures, bool auth); void doPrune(void); void doSlash(int perc); uint64_t doDump(int fd); @@ -48,7 +48,6 @@ private: struct StoredRecord { mutable uint32_t d_ttd; - string d_string; bool operator<(const StoredRecord& rhs) const @@ -70,7 +69,7 @@ private: {} typedef vector records_t; - + vector> d_signatures; uint32_t getTTD() const { if(d_records.size()==1) diff --git a/pdns/reczones.cc b/pdns/reczones.cc index a6e3291449..b38ea35f13 100644 --- a/pdns/reczones.cc +++ b/pdns/reczones.cc @@ -64,13 +64,13 @@ void primeHints(void) arr.content=ips[c-'a']; set aset; aset.insert(arr); - t_RC->replace(time(0), string(templ), QType(QType::A), aset, true); // auth, nuke it all + t_RC->replace(time(0), string(templ), QType(QType::A), aset, vector>(), true); // auth, nuke it all if (ip6s[c-'a'] != NULL) { aaaarr.content=ip6s[c-'a']; set aaaaset; aaaaset.insert(aaaarr); - t_RC->replace(time(0), string(templ), QType(QType::AAAA), aaaaset, true); + t_RC->replace(time(0), string(templ), QType(QType::AAAA), aaaaset, vector>(), true); } nsset.insert(nsrr); @@ -85,18 +85,18 @@ void primeHints(void) if(rr.qtype.getCode()==QType::A) { set aset; aset.insert(rr); - t_RC->replace(time(0), rr.qname, QType(QType::A), aset, true); // auth, etc see above + t_RC->replace(time(0), rr.qname, QType(QType::A), aset, vector>(), true); // auth, etc see above } else if(rr.qtype.getCode()==QType::AAAA) { set aaaaset; aaaaset.insert(rr); - t_RC->replace(time(0), rr.qname, QType(QType::AAAA), aaaaset, true); + t_RC->replace(time(0), rr.qname, QType(QType::AAAA), aaaaset, vector>(), true); } else if(rr.qtype.getCode()==QType::NS) { rr.content=toLower(rr.content); nsset.insert(rr); } } } - t_RC->replace(time(0),".", QType(QType::NS), nsset, true); // and stuff in the cache (auth) + t_RC->replace(time(0),".", QType(QType::NS), nsset, vector>(), true); // and stuff in the cache (auth) } static void makeNameToIPZone(SyncRes::domainmap_t* newMap, const string& hostname, const string& ip) diff --git a/pdns/syncres.cc b/pdns/syncres.cc index 65908af370..86ae430934 100644 --- a/pdns/syncres.cc +++ b/pdns/syncres.cc @@ -30,6 +30,7 @@ #include "syncres.hh" #include #include +#include "dnsrecords.hh" #include #include #include @@ -74,12 +75,11 @@ SyncRes::LogMode SyncRes::s_lm; #define LOG(x) if(d_lm == Log) { L <ednsstatus.begin(); iter != t_sstorage->ednsstatus.end(); ++iter) { - fprintf(fp, "%s\t%d\t%s", iter->first.toString().c_str(), (int)iter->second.mode, ctime(&iter->second.modeSetAt)); + for(const auto& eds : t_sstorage->ednsstatus) { + fprintf(fp, "%s\t%d\t%s", eds.first.toString().c_str(), (int)eds.second.mode, ctime(&eds.second.modeSetAt)); } fclose(fp); @@ -254,132 +253,77 @@ void SyncRes::doEDNSDumpAndClose(int fd) int SyncRes::asyncresolveWrapper(const ComboAddress& ip, const string& 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 + 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 + 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 '4', send bare queries + 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(); // cerr<<"Resetting EDNS Status for "<mode == EDNSStatus::UNKNOWN) - ednsstatus->mode = EDNSStatus::EDNSNOPING; - SyncRes::EDNSStatus::EDNSMode& mode=ednsstatus->mode; SyncRes::EDNSStatus::EDNSMode oldmode = mode; int EDNSLevel=0; int ret; for(int tries = 0; tries < 3; ++tries) { - // cerr<<"Remote '"<d_pingCorrect) { - L<d_rcode = RCode::ServFail; - g_stats.ednsPingMismatches++; - } - else { - g_stats.ednsPingMatches++; - ednsstatus->modeSetAt=d_now.tv_sec; // only the very best mode self-perpetuates - } - } - else if(mode==EDNSStatus::UNKNOWN || mode==EDNSStatus::EDNSPINGOK || mode == EDNSStatus::EDNSIGNORANT ) { - if(res->d_rcode == RCode::FormErr) { - // cerr<<"Downgrading to EDNSNOPING because of FORMERR!"<d_rcode == RCode::Refused || res->d_rcode == RCode::NotImp) ) { // this "fixes" F5 - // cerr<<"Downgrading an unknown status to EDNSNOPING because of RCODE="<d_rcode<d_rcode == RCode::FormErr || res->d_rcode == RCode::NotImp) { + cerr<<"Downgrading to NOEDNS because of FORMERR or NotImp!"<d_pingCorrect && res->d_haveEDNS) - mode = EDNSStatus::EDNSPINGOK; - else if(res->d_pingCorrect) { - L<d_haveEDNS) { if(mode != EDNSStatus::EDNSIGNORANT) { mode = EDNSStatus::EDNSIGNORANT; - // cerr<<"We find that "<d_rcode == RCode::FormErr) { - // cerr<<"Downgrading to mode 4, FORMERR!"<d_pingCorrect) { - // an upgrade! - L<modeSetAt=d_now.tv_sec; - // cerr<<"Result: ret="<d_haveEDNS<<", EDNS-PING correct: "<d_pingCorrect<<", new mode: "<d_haveEDNS<<", new mode: "<d_place == DNSResourceRecord::ANSWER) ret.push_back(*i); } @@ -757,14 +701,15 @@ bool SyncRes::doCacheCheck(const string &qname, const QType &qtype, vector cset; bool found=false, expired=false; - - if(t_RC->get(d_now.tv_sec, sqname, sqt, &cset) > 0) { + vector> signatures; + uint32_t ttl=0; + if(t_RC->get(d_now.tv_sec, sqname, sqt, &cset, d_doDNSSEC ? &signatures : 0) > 0) { LOG(prefix<::const_iterator j=cset.begin();j!=cset.end();++j) { LOG(j->content); if(j->ttl>(unsigned int) d_now.tv_sec) { DNSResourceRecord rr=*j; - rr.ttl-=d_now.tv_sec; + ttl=rr.ttl-=d_now.tv_sec; if(giveNegative) { rr.d_place=DNSResourceRecord::AUTHORITY; rr.ttl=sttl; @@ -778,6 +723,17 @@ bool SyncRes::doCacheCheck(const string &qname, const QType &qtype, vectorgetZoneRepresentation(); + rr.d_place=DNSResourceRecord::ANSWER; + + ret.push_back(rr); + } LOG(endl); if(found && !expired) { @@ -912,7 +868,7 @@ int SyncRes::doResolveAt(set nameservers, string auth, LWResult lwr; if(tns->empty()) { LOG(prefix< nameservers, string auth, if(s_maxtotusec && d_totUsec > s_maxtotusec) throw ImmediateServFailException("Too much time waiting for "+qname+"|"+qtype.getName()+", timeouts: "+boost::lexical_cast(d_timeouts) +", throttles: "+boost::lexical_cast(d_throttledqueries) + ", queries: "+lexical_cast(d_outqueries)+", "+lexical_cast(d_totUsec/1000)+"msec"); - if(d_pdl && d_pdl->preoutquery(*remoteIP, d_requestor, qname, qtype, lwr.d_result, resolveret)) { + if(d_pdl && d_pdl->preoutquery(*remoteIP, d_requestor, qname, qtype, lwr.getResult(), resolveret)) { LOG(prefix< nameservers, string auth, return RCode::ServFail; } - LOG(prefix<toString() <<"), rcode="<toString() <<"), rcode="<sin4.sin_family==AF_INET6) @@ -1070,16 +1026,31 @@ int SyncRes::doResolveAt(set nameservers, string auth, } if(s_minimumTTL) { - 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) { i->ttl = max(i->ttl, s_minimumTTL); } } - typedef map, set, TCacheComp > tcache_t; + struct CachePair + { + set records; + vector> signatures; + }; + typedef map, CachePair, TCacheComp > tcache_t; tcache_t tcache; + if(d_doDNSSEC) { + for(const auto& rec : lwr.d_records) { + if(rec.first.d_type == QType::RRSIG) { + auto rrsig = std::dynamic_pointer_cast(rec.first.d_content); + cerr<<"Got an RRSIG for "<d_type)<<" with name '"<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<<"' from '"< nameservers, string auth, s_nodelegated++; return RCode::NXDomain; } + else if(i->qtype.getCode() == QType::RRSIG) { + LOG("RRSIG - separate"< nameservers, string auth, 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 @@ -1118,24 +1092,27 @@ int SyncRes::doResolveAt(set nameservers, string auth, // 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::max(); - for(tcache_t::value_type::second_type::iterator j=i->second.begin(); j != i->second.end(); ++j) - lowestTTL=min(lowestTTL, j->ttl); + for(auto& record : i->second.records) + lowestTTL=min(lowestTTL, record.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) + *const_cast(&record.ttl)=lowestTTL; } - t_RC->replace(d_now.tv_sec, i->first.first, i->first.second, i->second, lwr.d_aabit); + cout<<"Have "<second.records.size()<<" records and "<second.signatures.size()<<" signatures for "<first.first; + cout<<'|'<first.second.getCode())<replace(d_now.tv_sec, i->first.first, i->first.second, i->second.records, i->second.signatures, lwr.d_aabit); } set nsset; LOG(prefix<d_place==DNSResourceRecord::AUTHORITY && i->qtype.getCode()==QType::SOA && lwr.d_rcode==RCode::NXDomain && dottedEndsOn(qname,i->qname) && dottedEndsOn(i->qname, auth)) { LOG(prefix< nameservers, string auth, 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) && ( @@ -1188,6 +1168,10 @@ int SyncRes::doResolveAt(set nameservers, string auth, LOG(prefix<qname<<"' -> '"<content<<"', had '"<content); } + else if(i->d_place==DNSResourceRecord::AUTHORITY && dottedEndsOn(qname,i->qname) && i->qtype.getCode()==QType::DS) { + LOG(prefix<qname<<"' -> '"<content<<"'"<d_place==DNSResourceRecord::AUTHORITY && dottedEndsOn(qname,i->qname) && i->qtype.getCode()==QType::SOA && lwr.d_rcode==RCode::NoError) { LOG(prefix< nameservers, string auth, } else if(realreferral) { LOG(prefix<dnssecmap[newauth]=true; + for(const auto& e : t_sstorage->dnssecmap) + cout< ednsstatus_t; @@ -434,6 +434,7 @@ public: throttle_t throttle; fails_t fails; domainmap_t* domainmap; + map dnssecmap; }; private: @@ -451,6 +452,7 @@ private: inline vector shuffleInSpeedOrder(set &nameservers, const string &prefix); bool moreSpecificThan(const string& a, const string &b); vector getAddrs(const string &qname, int depth, set& beenthere); + private: ostringstream d_trace; shared_ptr d_pdl; @@ -458,6 +460,7 @@ private: bool d_cacheonly; bool d_nocache; bool d_doEDNS0; + static LogMode s_lm; LogMode d_lm; -- 2.47.2