From: Remi Gacogne Date: Wed, 22 Mar 2017 15:44:24 +0000 (+0100) Subject: rec: Add ECS unit tests for `SyncRes` X-Git-Tag: rec-4.1.0-alpha1~174^2~11 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=e9f9b8ecc2f66b78963231a155b8c863b71b40da;p=pdns rec: Add ECS unit tests for `SyncRes` --- diff --git a/pdns/pdns_recursor.cc b/pdns/pdns_recursor.cc index 01ae81906..478bc4a2a 100644 --- a/pdns/pdns_recursor.cc +++ b/pdns/pdns_recursor.cc @@ -2810,6 +2810,9 @@ static int serviceMain(int argc, char*argv[]) SyncRes::s_serverID=tmp; } + SyncRes::s_ecsipv4limit = ::arg().asNum("ecs-ipv4-bits"); + SyncRes::s_ecsipv6limit = ::arg().asNum("ecs-ipv6-bits"); + g_networkTimeoutMsec = ::arg().asNum("network-timeout"); g_initialDomainMap = parseAuthAndForwards(); diff --git a/pdns/recursordist/ecs.cc b/pdns/recursordist/ecs.cc index f70e73272..7e9b7178b 100644 --- a/pdns/recursordist/ecs.cc +++ b/pdns/recursordist/ecs.cc @@ -5,41 +5,6 @@ NetmaskGroup g_ednssubnets; SuffixMatchNode g_ednsdomains; bool g_useIncomingECS; -boost::optional getEDNSSubnetMask(const ComboAddress& local, const DNSName&dn, const ComboAddress& rem, boost::optional incomingECS) -{ - static uint8_t l_ipv4limit, l_ipv6limit; - if(!l_ipv4limit) { - l_ipv4limit = ::arg().asNum("ecs-ipv4-bits"); - l_ipv6limit = ::arg().asNum("ecs-ipv6-bits"); - } - boost::optional result; - ComboAddress trunc; - uint8_t bits; - if(incomingECS) { - if (incomingECS->source.getBits() == 0) { - /* RFC7871 says we MUST NOT send any ECS if the source scope is 0 */ - return result; - } - trunc = incomingECS->source.getMaskedNetwork(); - bits = incomingECS->source.getBits(); - } - else if(!local.isIPv4() || local.sin4.sin_addr.s_addr) { // detect unset 'requestor' - trunc = local; - bits = local.isIPv4() ? 32 : 128; - } - else { - /* nothing usable */ - return result; - } - - if(g_ednsdomains.check(dn) || g_ednssubnets.match(rem)) { - bits = std::min(bits, (trunc.isIPv4() ? l_ipv4limit : l_ipv6limit)); - trunc.truncate(bits); - return boost::optional(Netmask(trunc, bits)); - } - return result; -} - void parseEDNSSubnetWhitelist(const std::string& wlist) { vector parts; diff --git a/pdns/recursordist/test-syncres_cc.cc b/pdns/recursordist/test-syncres_cc.cc index 7f531e087..2e634ab39 100644 --- a/pdns/recursordist/test-syncres_cc.cc +++ b/pdns/recursordist/test-syncres_cc.cc @@ -129,9 +129,12 @@ static void init(bool debug=false) SyncRes::s_serverdownmaxfails = 64; SyncRes::s_serverdownthrottletime = 60; SyncRes::s_doIPv6 = true; + SyncRes::s_ecsipv4limit = 24; + SyncRes::s_ecsipv6limit = 56; - ::arg().set("ecs-ipv4-bits", "24"); - ::arg().set("ecs-ipv6-bits", "56"); + g_ednssubnets = NetmaskGroup(); + g_ednsdomains = SuffixMatchNode(); + g_useIncomingECS = false; } static void initSR(std::unique_ptr& sr, bool edns0, bool dnssec) @@ -476,7 +479,7 @@ BOOST_AUTO_TEST_CASE(test_glued_referral) { int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret); BOOST_CHECK_EQUAL(res, 0); BOOST_REQUIRE_EQUAL(ret.size(), 1); - BOOST_CHECK_EQUAL(ret[0].d_type, QType::A); + BOOST_CHECK(ret[0].d_type == QType::A); BOOST_CHECK_EQUAL(ret[0].d_name, target); } @@ -545,10 +548,81 @@ BOOST_AUTO_TEST_CASE(test_glueless_referral) { int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret); BOOST_CHECK_EQUAL(res, 0); BOOST_REQUIRE_EQUAL(ret.size(), 1); - BOOST_CHECK_EQUAL(ret[0].d_type, QType::A); + BOOST_CHECK(ret[0].d_type == QType::A); BOOST_CHECK_EQUAL(ret[0].d_name, target); } +BOOST_AUTO_TEST_CASE(test_edns_submask_by_domain) { + std::unique_ptr sr; + init(); + initSR(sr, true, false); + + primeHints(); + + const DNSName target("powerdns.com."); + g_useIncomingECS = true; + g_ednsdomains.add(target); + + EDNSSubnetOpts incomingECS; + incomingECS.source = Netmask("192.0.2.128/32"); + sr->setIncomingECSFound(true); + sr->setIncomingECS(incomingECS); + + sr->setAsyncCallback([target](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional& srcmask, boost::optional context, std::shared_ptr outgoingLogger, LWResult* res) { + + BOOST_REQUIRE(srcmask); + BOOST_CHECK_EQUAL(srcmask->toString(), "192.0.2.0/24"); + return 0; + }); + + vector ret; + int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret); + BOOST_CHECK_EQUAL(res, 2); +} + +BOOST_AUTO_TEST_CASE(test_edns_submask_by_addr) { + std::unique_ptr sr; + init(); + initSR(sr, true, false); + + primeHints(); + + const DNSName target("powerdns.com."); + g_useIncomingECS = true; + g_ednssubnets.addMask("192.0.2.1/32"); + + EDNSSubnetOpts incomingECS; + incomingECS.source = Netmask("2001:DB8::FF/128"); + sr->setIncomingECSFound(true); + sr->setIncomingECS(incomingECS); + + sr->setAsyncCallback([target](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional& srcmask, boost::optional context, std::shared_ptr outgoingLogger, LWResult* res) { + + if (isRootServer(ip)) { + BOOST_REQUIRE(!srcmask); + + setLWResult(res, 0, true, false, true); + addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800); + addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600); + return 1; + } else if (ip == ComboAddress("192.0.2.1:53")) { + + BOOST_REQUIRE(srcmask); + BOOST_CHECK_EQUAL(srcmask->toString(), "2001:db8::/56"); + + setLWResult(res, 0, true, false, false); + addRecordToLW(res, domain, QType::A, "192.0.2.2"); + return 1; + } + + return 0; + }); + + vector ret; + int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret); + BOOST_CHECK_EQUAL(res, 0); +} + /* TODO: @@ -571,8 +645,6 @@ check we query the fastest auth available first? check we correctly store slow servers -check EDNS subnetmask - if possible, check preoutquery check depth limit diff --git a/pdns/syncres.cc b/pdns/syncres.cc index f88900e41..8b1c95ef2 100644 --- a/pdns/syncres.cc +++ b/pdns/syncres.cc @@ -67,6 +67,8 @@ std::atomic SyncRes::s_throttledqueries; std::atomic SyncRes::s_dontqueries; std::atomic SyncRes::s_nodelegated; std::atomic SyncRes::s_unreachables; +uint8_t SyncRes::s_ecsipv4limit; +uint8_t SyncRes::s_ecsipv6limit; unsigned int SyncRes::s_minimumTTL; bool SyncRes::s_doIPv6; bool SyncRes::s_nopacketcache; @@ -1407,7 +1409,7 @@ int SyncRes::doResolveAt(NsSet &nameservers, DNSName auth, bool flawedNSSet, con LOG(prefix<toString()<<" to query"< SyncRes::getEDNSSubnetMask(const ComboAddress& local, const DNSName&dn, const ComboAddress& rem) +{ + boost::optional result; + ComboAddress trunc; + uint8_t bits; + if(d_incomingECSFound) { + if (d_incomingECS->source.getBits() == 0) { + /* RFC7871 says we MUST NOT send any ECS if the source scope is 0 */ + return result; + } + trunc = d_incomingECS->source.getMaskedNetwork(); + bits = d_incomingECS->source.getBits(); + } + else if(!local.isIPv4() || local.sin4.sin_addr.s_addr) { // detect unset 'requestor' + trunc = local; + bits = local.isIPv4() ? 32 : 128; + } + else { + /* nothing usable */ + return result; + } + + if(g_ednsdomains.check(dn) || g_ednssubnets.match(rem)) { + bits = std::min(bits, (trunc.isIPv4() ? s_ecsipv4limit : s_ecsipv6limit)); + trunc.truncate(bits); + return boost::optional(Netmask(trunc, bits)); + } + + return result; +} // used by PowerDNSLua - note that this neglects to add the packet count & statistics back to pdns_ercursor.cc int directResolve(const DNSName& qname, const QType& qtype, int qclass, vector& ret) diff --git a/pdns/syncres.hh b/pdns/syncres.hh index d32e913c3..9910f5c09 100644 --- a/pdns/syncres.hh +++ b/pdns/syncres.hh @@ -544,6 +544,8 @@ public: static unsigned int s_packetcacheservfailttl; static unsigned int s_serverdownmaxfails; static unsigned int s_serverdownthrottletime; + static uint8_t s_ecsipv4limit; + static uint8_t s_ecsipv6limit; static bool s_nopacketcache; static string s_serverID; @@ -585,6 +587,8 @@ private: int asyncresolveWrapper(const ComboAddress& ip, bool ednsMANDATORY, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, struct timeval* now, boost::optional& srcmask, LWResult* res) const; + boost::optional getEDNSSubnetMask(const ComboAddress& local, const DNSName&dn, const ComboAddress& rem); + ostringstream d_trace; shared_ptr d_pdl; boost::optional d_incomingECS; @@ -808,7 +812,6 @@ uint64_t* pleaseWipeCache(const DNSName& canon, bool subtree=false); uint64_t* pleaseWipePacketCache(const DNSName& canon, bool subtree); uint64_t* pleaseWipeAndCountNegCache(const DNSName& canon, bool subtree=false); void doCarbonDump(void*); -boost::optional getEDNSSubnetMask(const ComboAddress& local, const DNSName&dn, const ComboAddress& rem, boost::optional incomingECS); void parseEDNSSubnetWhitelist(const std::string& wlist); extern __thread struct timeval g_now;