}
SyncRes::s_minimumTTL = ::arg().asNum("minimum-ttl-override");
+ SyncRes::s_minimumECSTTL = ::arg().asNum("ecs-minimum-ttl-override");
SyncRes::s_nopacketcache = ::arg().mustDo("disable-packetcache");
::arg().setSwitch( "disable-packetcache", "Disable packetcache" )= "no";
::arg().set("ecs-ipv4-bits", "Number of bits of IPv4 address to pass for EDNS Client Subnet")="24";
::arg().set("ecs-ipv6-bits", "Number of bits of IPv6 address to pass for EDNS Client Subnet")="56";
+ ::arg().set("ecs-minimum-ttl-override", "Set under adverse conditions, a minimum TTL for records in ECS-specific answers")="0";
::arg().set("edns-subnet-whitelist", "List of netmasks and domains that we should enable EDNS subnet for")="";
::arg().set("ecs-add-for", "List of client netmasks for which EDNS Client Subnet will be added")="0.0.0.0/0, ::/0, " LOCAL_NETS_INVERSE;
::arg().set("ecs-scope-zero-address", "Address to send to whitelisted authoritative servers for incoming queries with ECS prefix-length source of 0")="";
template<typename T>
static string setMinimumTTL(T begin, T end)
{
- if(end-begin != 1)
+ if(end-begin != 1)
return "Need to supply new minimum TTL number\n";
SyncRes::s_minimumTTL = pdns_stou(*begin);
return "New minimum TTL: " + std::to_string(SyncRes::s_minimumTTL) + "\n";
}
+template<typename T>
+static string setMinimumECSTTL(T begin, T end)
+{
+ if(end-begin != 1)
+ return "Need to supply new ECS minimum TTL number\n";
+ SyncRes::s_minimumECSTTL = pdns_stou(*begin);
+ return "New minimum ECS TTL: " + std::to_string(SyncRes::s_minimumECSTTL) + "\n";
+}
+
template<typename T>
static string setMaxCacheEntries(T begin, T end)
{
"reload-lua-script [filename] (re)load Lua script\n"
"reload-lua-config [filename] (re)load Lua configuration file\n"
"reload-zones reload all auth and forward zones\n"
+"set-ecs-minimum-ttl value set ecs-minimum-ttl-override\n"
"set-max-cache-entries value set new maximum cache size\n"
"set-max-packetcache-entries val set new maximum packet cache size\n"
"set-minimum-ttl value set minimum-ttl-override\n"
return reloadAuthAndForwards();
}
+ if(cmd=="set-ecs-minimum-ttl") {
+ return setMinimumECSTTL(begin, end);
+ }
+
if(cmd=="set-max-cache-entries") {
return setMaxCacheEntries(begin, end);
}
DNSSEC validation failures and to 'no' or 'off' to disable logging these
failures.
+set-ecs-minimum-ttl *NUM*
+ Set ecs-minimum-ttl-override to *NUM*.
+
set-max-cache-entries *NUM*
Change the maximum number of entries in the DNS cache. If reduced, the
cache size will start shrinking to this number as part of the normal
Number of bits of client IPv6 address to pass when sending EDNS Client Subnet address information.
+.. _setting-ecs-minimum-ttl-override:
+
+``ecs-minimum-ttl-override``
+----------------------------
+- Integer
+- Default: 0 (disabled)
+
+This setting artificially raises the TTLs of records in the ANSWER section of ECS-specific answers to be at least this long.
+While this is a gross hack, and violates RFCs, under conditions of DoS, it may enable you to continue serving your customers.
+Can be set at runtime using ``rec_control set-ecs-minimum-ttl 3600``.
+
.. _setting-ecs-scope-zero-address:
``ecs-scope-zero-address``
SyncRes::s_ecsipv6limit = 56;
SyncRes::s_rootNXTrust = true;
SyncRes::s_minimumTTL = 0;
+ SyncRes::s_minimumECSTTL = 0;
SyncRes::s_serverID = "PowerDNS Unit Tests Server ID";
SyncRes::clearEDNSLocalSubnets();
SyncRes::addEDNSLocalSubnet("0.0.0.0/0");
BOOST_CHECK_LE((cached[0].d_ttl - now), SyncRes::s_maxcachettl);
}
+BOOST_AUTO_TEST_CASE(test_cache_min_max_ecs_ttl) {
+ std::unique_ptr<SyncRes> sr;
+ initSR(sr);
+
+ primeHints();
+
+ const DNSName target("cacheecsttl.powerdns.com.");
+ const ComboAddress ns("192.0.2.1:53");
+
+ EDNSSubnetOpts incomingECS;
+ incomingECS.source = Netmask("192.0.2.128/32");
+ sr->setQuerySource(ComboAddress(), boost::optional<const EDNSSubnetOpts&>(incomingECS));
+ SyncRes::addEDNSDomain(target);
+
+ sr->setAsyncCallback([target,ns](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, LWResult* res, bool* chained) {
+
+ BOOST_REQUIRE(srcmask);
+ BOOST_CHECK_EQUAL(srcmask->toString(), "192.0.2.0/24");
+
+ if (isRootServer(ip)) {
+
+ setLWResult(res, 0, false, false, true);
+ addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
+ addRecordToLW(res, "a.gtld-servers.net.", QType::A, ns.toString(), DNSResourceRecord::ADDITIONAL, 20);
+ srcmask = boost::none;
+
+ return 1;
+ } else if (ip == ns) {
+
+ setLWResult(res, 0, true, false, false);
+ addRecordToLW(res, domain, QType::A, "192.0.2.2", DNSResourceRecord::ANSWER, 10);
+
+ return 1;
+ }
+
+ return 0;
+ });
+
+ const time_t now = sr->getNow().tv_sec;
+ SyncRes::s_minimumTTL = 60;
+ SyncRes::s_minimumECSTTL = 120;
+ SyncRes::s_maxcachettl = 3600;
+
+ vector<DNSRecord> ret;
+ int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+ BOOST_CHECK_EQUAL(res, RCode::NoError);
+ BOOST_REQUIRE_EQUAL(ret.size(), 1);
+ BOOST_CHECK_EQUAL(ret[0].d_ttl, SyncRes::s_minimumECSTTL);
+
+ const ComboAddress who("192.0.2.128");
+ vector<DNSRecord> cached;
+ BOOST_REQUIRE_GT(t_RC->get(now, target, QType(QType::A), true, &cached, who), 0);
+ BOOST_REQUIRE_EQUAL(cached.size(), 1);
+ BOOST_REQUIRE_GT(cached[0].d_ttl, now);
+ BOOST_CHECK_EQUAL((cached[0].d_ttl - now), SyncRes::s_minimumECSTTL);
+
+ cached.clear();
+ BOOST_REQUIRE_GT(t_RC->get(now, target, QType(QType::NS), false, &cached, who), 0);
+ BOOST_REQUIRE_EQUAL(cached.size(), 1);
+ BOOST_REQUIRE_GT(cached[0].d_ttl, now);
+ BOOST_CHECK_LE((cached[0].d_ttl - now), SyncRes::s_maxcachettl);
+
+ cached.clear();
+ BOOST_REQUIRE_GT(t_RC->get(now, DNSName("a.gtld-servers.net."), QType(QType::A), false, &cached, who), 0);
+ BOOST_REQUIRE_EQUAL(cached.size(), 1);
+ BOOST_REQUIRE_GT(cached[0].d_ttl, now);
+ BOOST_CHECK_LE((cached[0].d_ttl - now), SyncRes::s_minimumTTL);
+}
+
BOOST_AUTO_TEST_CASE(test_cache_expired_ttl) {
std::unique_ptr<SyncRes> sr;
initSR(sr);
unsigned int SyncRes::s_maxtotusec;
unsigned int SyncRes::s_maxdepth;
unsigned int SyncRes::s_minimumTTL;
+unsigned int SyncRes::s_minimumECSTTL;
unsigned int SyncRes::s_packetcachettl;
unsigned int SyncRes::s_packetcacheservfailttl;
unsigned int SyncRes::s_serverdownmaxfails;
}
}
+ /* if the answer is ECS-specific, a minimum TTL is set for this kind of answers
+ and it's higher than the global minimum TTL */
+ if (ednsmask && s_minimumECSTTL > 0 && (s_minimumTTL == 0 || s_minimumECSTTL > s_minimumTTL)) {
+ for(auto& rec : lwr.d_records) {
+ if (rec.d_place == DNSResourceRecord::ANSWER) {
+ rec.d_ttl = max(rec.d_ttl, s_minimumECSTTL);
+ }
+ }
+ }
+
bool needWildcardProof = false;
unsigned int wildcardLabelsCount;
*rcode = updateCacheFromRecords(depth, lwr, qname, qtype, auth, wasForwarded, ednsmask, state, needWildcardProof, wildcardLabelsCount, sendRDQuery);
static string s_serverID;
static unsigned int s_minimumTTL;
+ static unsigned int s_minimumECSTTL;
static unsigned int s_maxqperq;
static unsigned int s_maxtotusec;
static unsigned int s_maxdepth;