Also, improve EDNS probing, plus make recursor packet cache DNSSEC aware.
#include "dns_random.hh"
#include <boost/scoped_array.hpp>
#include <boost/algorithm/string.hpp>
+#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
srcmask=boost::optional<Netmask>(); // 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;
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;
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<<Logger::Warning<<"Failed to validate "<<dc->d_mdp.d_qname<<" for "<<dc->d_remote.toStringWithPort()<<endl;
+ }
}
}
}
sendmsg(dc->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
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<<Logger::Notice<<t_id<< " question answered from packet cache from "<<fromaddr.toString()<<endl;
// t_queryring->push_back("packetcached");
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<<Logger::Error<<"Unknown DNSSEC mode "<<::arg()["dnssec"]<<endl;
+ exit(1);
+ }
+
if(::arg()["trace"]=="fail") {
SyncRes::setDefaultLogMode(SyncRes::Store);
}
::arg().set("local-address","IP addresses to listen on, separated by spaces or commas. Also accepts ports.")="127.0.0.1";
::arg().setSwitch("non-local-bind", "Enable binding to non-local addresses by using FREEBIND / BINDANY socket options")="no";
::arg().set("trace","if we should output heaps of logging. set to 'fail' to only log failing domains")="off";
+ ::arg().set("dnssec", "DNSSEC mode: off/process (default)/log-fail/validate")="process";
::arg().set("daemon","Operate as a daemon")="yes";
::arg().setSwitch("write-pid","Write a PID file")="yes";
::arg().set("loglevel","Amount of logging. Higher is more. Do not set below 3")="4";
#include "syncres.hh"
#include "rpzloader.hh"
-GlobalStateHolder<LuaConfigItems> g_luaconfs;
+GlobalStateHolder<LuaConfigItems> 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
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<const struct dnsheader*>((*iter).d_packet.c_str());
if(packet->qdcount==0)
break;
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);
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);
{
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);
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
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);
fclose(fp);
}
-int SyncRes::asyncresolveWrapper(const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, struct timeval* now, boost::optional<Netmask>& 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<Netmask>& srcmask, LWResult* res)
{
/* what is your QUEST?
the goal is to get as many remotes as possible on the highest level of EDNS support
for(int tries = 0; tries < 3; ++tries) {
// cerr<<"Remote '"<<ip.toString()<<"' currently in mode "<<mode<<endl;
- if(mode==EDNSStatus::UNKNOWN || mode==EDNSStatus::EDNSOK || mode==EDNSStatus::EDNSIGNORANT)
+ if(ednsMANDATORY || mode==EDNSStatus::UNKNOWN || mode==EDNSStatus::EDNSOK || mode==EDNSStatus::EDNSIGNORANT)
EDNSLevel = 1;
else if(mode==EDNSStatus::NOEDNS) {
g_stats.noEdnsOutQueries++;
ret=asyncresolve(ip, domain, type, doTCP, sendRDQuery, EDNSLevel, now, srcmask, res);
if(ret == 0 || ret < 0) {
- // cerr<< (ret < 0 ? "Transport error" : "Timeout")<<" for query to "<<ip.toString()<<" for '"<<domain.toString()<<"' (ret="<<ret<<"), no change in mode"<<endl;
+ cerr<< (ret < 0 ? "Transport error" : "Timeout")<<" for query to "<<ip.toString()<<" for '"<<domain.toString()<<"' (ret = "<<ret<<", mode = "<<(int)mode<<")"<<endl;
+ // timeout = downgrade to EDNS
+ if(ret==0 && mode != EDNSStatus::NOEDNS) {
+ // cerr<<"\tDowngrading to NOEDNS"<<endl;
+ mode = EDNSStatus::NOEDNS;
+ continue;
+ }
return ret;
}
else if(mode==EDNSStatus::UNKNOWN || mode==EDNSStatus::EDNSOK || mode == EDNSStatus::EDNSIGNORANT ) {
}
}
- if(oldmode != mode)
+ if(oldmode != mode || !ednsstatus->modeSetAt)
ednsstatus->modeSetAt=d_now.tv_sec;
// cerr<<"Result: ret="<<ret<<", EDNS-level: "<<EDNSLevel<<", haveEDNS: "<<res->d_haveEDNS<<", new mode: "<<mode<<endl;
return ret;
LOG(prefix<<qname.toString()<<": forwarding query to hardcoded nameserver '"<< remoteIP.toStringWithPort()<<"' for zone '"<<authname.toString()<<"'"<<endl);
boost::optional<Netmask> 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) {
}
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)
typedef map<CacheKey, CachePair> 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<RRSIGRecordContent>(rec.d_content);
- // cerr<<"Got an RRSIG for "<<DNSRecordContent::NumberToType(rrsig->d_type)<<" with name '"<<rec.d_name<<"'"<<endl;
- tcache[{rec.d_name, rrsig->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<RRSIGRecordContent>(rec.d_content);
+ // cerr<<"Got an RRSIG for "<<DNSRecordContent::NumberToType(rrsig->d_type)<<" with name '"<<rec.d_name<<"'"<<endl;
+ tcache[{rec.d_name, rrsig->d_type, rec.d_place}].signatures.push_back(rrsig);
+ }
}
// reap all answers from this packet that are acceptable
return d_wasVariable;
}
- int asyncresolveWrapper(const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, struct timeval* now, boost::optional<Netmask>& srcmask, LWResult* res);
+ int asyncresolveWrapper(const ComboAddress& ip, bool ednsMANDATORY, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, struct timeval* now, boost::optional<Netmask>& srcmask, LWResult* res);
static void doEDNSDumpAndClose(int fd);
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);
#include "validate-recursor.hh"
#include "syncres.hh"
+DNSSECMode g_dnssecmode{DNSSECMode::Process};
+
class SRRecordOracle : public DNSRecordOracle
{
public:
#include "validate.hh"
vState validateRecords(const vector<DNSRecord>& 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;