From 12ce523e77d7738d78e436a9fe1dd25818a8f2cd Mon Sep 17 00:00:00 2001 From: bert hubert Date: Mon, 14 Dec 2015 15:49:37 +0100 Subject: [PATCH] implement four dnssec modes: off (3.x behaviour), process (ask for DNSSEC, give it when asked for, validate when asked to), validate (always validate), log-fail, always validate but log failures only. Also, improve EDNS probing, plus make recursor packet cache DNSSEC aware. --- pdns/lwres.cc | 3 ++- pdns/pdns_recursor.cc | 38 +++++++++++++++++++++++++++++++++----- pdns/rec-lua-conf.cc | 2 +- pdns/recpacketcache.cc | 10 ++++++---- pdns/recpacketcache.hh | 14 +++++++------- pdns/syncres.cc | 32 ++++++++++++++++++-------------- pdns/syncres.hh | 4 +++- pdns/validate-recursor.cc | 2 ++ pdns/validate-recursor.hh | 9 +++++++++ 9 files changed, 81 insertions(+), 33 deletions(-) diff --git a/pdns/lwres.cc b/pdns/lwres.cc index 6bd17220a..2d136bfa2 100644 --- a/pdns/lwres.cc +++ b/pdns/lwres.cc @@ -47,6 +47,7 @@ #include "dns_random.hh" #include #include +#include "validate-recursor.hh" #include "ednssubnet.hh" //! returns -2 for OS limits error, -1 for permanent error that has to do with remote **transport**, 0 for timeout, 1 for success @@ -77,7 +78,7 @@ int asyncresolve(const ComboAddress& ip, const DNSName& domain, int type, bool d srcmask=boost::optional(); // this is also our return value } - pw.addOpt(g_outgoingEDNSBufsize, 0, EDNSOpts::DNSSECOK, opts); + pw.addOpt(g_outgoingEDNSBufsize, 0, g_dnssecmode == DNSSECMode::Off ? 0 : EDNSOpts::DNSSECOK, opts); pw.commit(); } lwr->d_rcode = 0; diff --git a/pdns/pdns_recursor.cc b/pdns/pdns_recursor.cc index 7dab9f0a8..a6cf25a13 100644 --- a/pdns/pdns_recursor.cc +++ b/pdns/pdns_recursor.cc @@ -810,7 +810,7 @@ void startDoResolve(void *p) pw.getHeader()->rcode=res; if(haveEDNS) { - if(edo.d_Z & EDNSOpts::DNSSECOK) { + if(g_dnssecmode != DNSSECMode::Off && ((edo.d_Z & EDNSOpts::DNSSECOK) || g_dnssecmode == DNSSECMode::ValidateAll || g_dnssecmode==DNSSECMode::ValidateForLog)) { auto state=validateRecords(ret); if(state == Secure) { pw.getHeader()->ad=1; @@ -819,8 +819,13 @@ void startDoResolve(void *p) pw.getHeader()->ad=0; } else if(state == Bogus && !pw.getHeader()->cd) { - pw.getHeader()->rcode=RCode::ServFail; - goto sendit; + if(g_dnssecmode == DNSSECMode::ValidateAll || (edo.d_Z & EDNSOpts::DNSSECOK)) { + pw.getHeader()->rcode=RCode::ServFail; + goto sendit; + } + else { + L<d_mdp.d_qname<<" for "<d_remote.toStringWithPort()<d_socket, &msgh, 0); if(!SyncRes::s_nopacketcache && !variableAnswer && !sr.wasVariable() ) { t_packetCache->insertResponsePacket(string((const char*)&*packet.begin(), packet.size()), + (edo.d_Z & EDNSOpts::DNSSECOK), // ponder filtering on dnssecmode here g_now.tv_sec, min(minTTL, (pw.getHeader()->rcode == RCode::ServFail) ? SyncRes::s_packetcacheservfailttl : SyncRes::s_packetcachettl @@ -1142,8 +1148,16 @@ string* doProcessUDPQuestion(const std::string& question, const ComboAddress& fr g_mtracer->clearAllocators(); */ #endif - - if(!SyncRes::s_nopacketcache && t_packetCache->getResponsePacket(question, g_now.tv_sec, &response, &age)) { + bool needsDNSSEC=false; + const struct dnsheader* dh = (struct dnsheader*)question.c_str(); + if(dh->arcount) { + unsigned int consumed=0; + DNSName qname(question.c_str(), question.length(), sizeof(dnsheader), false, 0, 0, &consumed); + if(question.size() > (consumed+12+11) && ((question[consumed+12+11]&0x80)==0x80)) + needsDNSSEC=true; + } + + if(!SyncRes::s_nopacketcache && t_packetCache->getResponsePacket(question, needsDNSSEC, g_now.tv_sec, &response, &age)) { if(!g_quiet) L<push_back("packetcached"); @@ -2213,6 +2227,19 @@ int serviceMain(int argc, char*argv[]) setupDelegationOnly(); g_outgoingEDNSBufsize=::arg().asNum("edns-outgoing-bufsize"); + if(::arg()["dnssec"]=="off") + g_dnssecmode=DNSSECMode::Off; + else if(::arg()["dnssec"]=="process") + g_dnssecmode=DNSSECMode::Process; + else if(::arg()["dnssec"]=="validate") + g_dnssecmode=DNSSECMode::ValidateAll; + else if(::arg()["dnssec"]=="log-fail") + g_dnssecmode=DNSSECMode::ValidateForLog; + else { + L< g_luaconfs; +GlobalStateHolder g_luaconfs; /* SO HOW DOES THIS WORK! AND PLEASE PAY ATTENTION! This function can be called at any time. It is expected to overwrite all the contents diff --git a/pdns/recpacketcache.cc b/pdns/recpacketcache.cc index 470bb9591..8926117f7 100644 --- a/pdns/recpacketcache.cc +++ b/pdns/recpacketcache.cc @@ -22,11 +22,11 @@ int RecursorPacketCache::doWipePacketCache(const DNSName& name, uint16_t qtype, pw.getHeader()->rd=1; Entry e; e.d_packet.assign((const char*)&*packet.begin(), packet.size()); - + e.d_wantsDNSSEC=false; // so the idea is, we search for a packet with qtype=0, which is ahead of anything with that name int count=0; - for(packetCache_t::iterator iter = d_packetCache.lower_bound(e); iter != d_packetCache.end(); ) { + for(auto iter = d_packetCache.lower_bound(e); iter != d_packetCache.end(); ) { const struct dnsheader* packet = reinterpret_cast((*iter).d_packet.c_str()); if(packet->qdcount==0) break; @@ -55,11 +55,12 @@ int RecursorPacketCache::doWipePacketCache(const DNSName& name, uint16_t qtype, return count; } -bool RecursorPacketCache::getResponsePacket(const std::string& queryPacket, time_t now, +bool RecursorPacketCache::getResponsePacket(const std::string& queryPacket, bool wantsDNSSEC, time_t now, std::string* responsePacket, uint32_t* age) { struct Entry e; e.d_packet=queryPacket; + e.d_wantsDNSSEC = wantsDNSSEC; packetCache_t::const_iterator iter = d_packetCache.find(e); @@ -96,10 +97,11 @@ bool RecursorPacketCache::getResponsePacket(const std::string& queryPacket, time return false; } -void RecursorPacketCache::insertResponsePacket(const std::string& responsePacket, time_t now, uint32_t ttl) +void RecursorPacketCache::insertResponsePacket(const std::string& responsePacket, bool wantsDNSSEC, time_t now, uint32_t ttl) { struct Entry e; e.d_packet = responsePacket; + e.d_wantsDNSSEC = wantsDNSSEC; e.d_ttd = now+ttl; e.d_creation = now; packetCache_t::iterator iter = d_packetCache.find(e); diff --git a/pdns/recpacketcache.hh b/pdns/recpacketcache.hh index aa17ff031..cc0cb96f8 100644 --- a/pdns/recpacketcache.hh +++ b/pdns/recpacketcache.hh @@ -23,8 +23,8 @@ class RecursorPacketCache { public: RecursorPacketCache(); - bool getResponsePacket(const std::string& queryPacket, time_t now, std::string* responsePacket, uint32_t* age); - void insertResponsePacket(const std::string& responsePacket, time_t now, uint32_t ttd); + bool getResponsePacket(const std::string& queryPacket, bool wantsDNSSEC, time_t now, std::string* responsePacket, uint32_t* age); + void insertResponsePacket(const std::string& responsePacket, bool wantsDNSSEC, time_t now, uint32_t ttd); void doPruneTo(unsigned int maxSize=250000); int doWipePacketCache(const DNSName& name, uint16_t qtype=0xffff, bool subtree=false); @@ -40,7 +40,7 @@ private: mutable uint32_t d_ttd; mutable uint32_t d_creation; mutable std::string d_packet; // "I know what I am doing" - + bool d_wantsDNSSEC; inline bool operator<(const struct Entry& rhs) const; uint32_t getTTD() const @@ -66,12 +66,12 @@ inline bool RecursorPacketCache::Entry::operator<(const struct RecursorPacketCac const struct dnsheader* dh=(const struct dnsheader*) d_packet.c_str(), *rhsdh=(const struct dnsheader*)rhs.d_packet.c_str(); - if(std::tie(dh->opcode, dh->rd, dh->qdcount) < - std::tie(rhsdh->opcode, rhsdh->rd, rhsdh->qdcount)) + if(std::tie(d_wantsDNSSEC, dh->opcode, dh->rd, dh->qdcount) < + std::tie(rhs.d_wantsDNSSEC, rhsdh->opcode, rhsdh->rd, rhsdh->qdcount)) return true; - if(std::tie(dh->opcode, dh->rd, dh->qdcount) > - std::tie(rhsdh->opcode, rhsdh->rd, rhsdh->qdcount)) + if(std::tie(d_wantsDNSSEC, dh->opcode, dh->rd, dh->qdcount) > + std::tie(rhs.d_wantsDNSSEC, rhsdh->opcode, rhsdh->rd, rhsdh->qdcount)) return false; return dnspacketLessThan(d_packet, rhs.d_packet); diff --git a/pdns/syncres.cc b/pdns/syncres.cc index ad34208de..a974da6c7 100644 --- a/pdns/syncres.cc +++ b/pdns/syncres.cc @@ -291,7 +291,7 @@ void SyncRes::doEDNSDumpAndClose(int fd) fclose(fp); } -int SyncRes::asyncresolveWrapper(const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, struct timeval* now, boost::optional& srcmask, LWResult* res) +int SyncRes::asyncresolveWrapper(const ComboAddress& ip, bool ednsMANDATORY, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, struct timeval* now, boost::optional& srcmask, LWResult* res) { /* what is your QUEST? the goal is to get as many remotes as possible on the highest level of EDNS support @@ -331,7 +331,7 @@ int SyncRes::asyncresolveWrapper(const ComboAddress& ip, const DNSName& domain, for(int tries = 0; tries < 3; ++tries) { // cerr<<"Remote '"<modeSetAt) ednsstatus->modeSetAt=d_now.tv_sec; // cerr<<"Result: ret="<d_haveEDNS<<", new mode: "< nm; - res=asyncresolveWrapper(remoteIP, qname, qtype.getCode(), false, false, &d_now, nm, &lwr); + res=asyncresolveWrapper(remoteIP, d_doDNSSEC, qname, qtype.getCode(), false, false, &d_now, nm, &lwr); // filter out the good stuff from lwr.result() for(const auto& rec : lwr.d_records) { @@ -1033,7 +1039,7 @@ int SyncRes::doResolveAt(set nameservers, DNSName auth, bool flawedNSSe } else { ednsmask=getEDNSSubnetMask(d_requestor, qname, *remoteIP); - resolveret=asyncresolveWrapper(*remoteIP, qname, qtype.getCode(), + resolveret=asyncresolveWrapper(*remoteIP, d_doDNSSEC, qname, qtype.getCode(), doTCP, sendRDQuery, &d_now, ednsmask, &lwr); // <- we go out on the wire! } if(resolveret==-3) @@ -1139,14 +1145,12 @@ int SyncRes::doResolveAt(set nameservers, DNSName auth, bool flawedNSSe typedef map tcache_t; tcache_t tcache; - if(d_doDNSSEC) { - for(const auto& rec : lwr.d_records) { - if(rec.d_type == QType::RRSIG) { - auto rrsig = std::dynamic_pointer_cast(rec.d_content); - // cerr<<"Got an RRSIG for "<d_type)<<" with name '"<d_type, rec.d_place}].signatures.push_back(rrsig); - } - } + for(const auto& rec : lwr.d_records) { + if(rec.d_type == QType::RRSIG) { + auto rrsig = std::dynamic_pointer_cast(rec.d_content); + // cerr<<"Got an RRSIG for "<d_type)<<" with name '"<d_type, rec.d_place}].signatures.push_back(rrsig); + } } // reap all answers from this packet that are acceptable diff --git a/pdns/syncres.hh b/pdns/syncres.hh index 16602965d..4612c2741 100644 --- a/pdns/syncres.hh +++ b/pdns/syncres.hh @@ -302,7 +302,7 @@ public: return d_wasVariable; } - int asyncresolveWrapper(const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, struct timeval* now, boost::optional& srcmask, LWResult* res); + int asyncresolveWrapper(const ComboAddress& ip, bool ednsMANDATORY, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, struct timeval* now, boost::optional& srcmask, LWResult* res); static void doEDNSDumpAndClose(int fd); @@ -645,6 +645,8 @@ extern RecursorStats g_stats; extern unsigned int g_numThreads; extern SuffixMatchNode g_delegationOnly; extern uint16_t g_outgoingEDNSBufsize; + + std::string reloadAuthAndForwards(); ComboAddress parseIPAndPort(const std::string& input, uint16_t port); ComboAddress getQueryLocalAddress(int family, uint16_t port); diff --git a/pdns/validate-recursor.cc b/pdns/validate-recursor.cc index 6d056be23..232549af2 100644 --- a/pdns/validate-recursor.cc +++ b/pdns/validate-recursor.cc @@ -2,6 +2,8 @@ #include "validate-recursor.hh" #include "syncres.hh" +DNSSECMode g_dnssecmode{DNSSECMode::Process}; + class SRRecordOracle : public DNSRecordOracle { public: diff --git a/pdns/validate-recursor.hh b/pdns/validate-recursor.hh index 195206144..a892cb54e 100644 --- a/pdns/validate-recursor.hh +++ b/pdns/validate-recursor.hh @@ -4,3 +4,12 @@ #include "validate.hh" vState validateRecords(const vector& recs); + +/* Off: 3.x behaviour, we do no DNSSEC, no EDNS + Process: we gather DNSSEC records on all queries, of you do do=1, we'll validate for you (unless you set cd=1) + ValidateForLog: Process + validate all answers, but only log failures + ValidateAll: DNSSEC issue -> servfail +*/ + +enum class DNSSECMode { Off, Process, ValidateForLog, ValidateAll }; +extern DNSSECMode g_dnssecmode; -- 2.40.0