SuffixMatchNode g_ednsdomains;
bool g_useIncomingECS;
-boost::optional<Netmask> getEDNSSubnetMask(const ComboAddress& local, const DNSName&dn, const ComboAddress& rem, boost::optional<const EDNSSubnetOpts&> 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<Netmask> 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>(Netmask(trunc, bits));
- }
- return result;
-}
-
void parseEDNSSubnetWhitelist(const std::string& wlist)
{
vector<string> parts;
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<SyncRes>& sr, bool edns0, bool dnssec)
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);
}
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<SyncRes> 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<Netmask>& srcmask, boost::optional<const ResolveContext&> context, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult* res) {
+
+ BOOST_REQUIRE(srcmask);
+ BOOST_CHECK_EQUAL(srcmask->toString(), "192.0.2.0/24");
+ return 0;
+ });
+
+ vector<DNSRecord> 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<SyncRes> 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<Netmask>& srcmask, boost::optional<const ResolveContext&> context, std::shared_ptr<RemoteLogger> 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<DNSRecord> ret;
+ int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+ BOOST_CHECK_EQUAL(res, 0);
+}
+
/*
TODO:
check we correctly store slow servers
-check EDNS subnetmask
-
if possible, check preoutquery
check depth limit
std::atomic<uint64_t> SyncRes::s_dontqueries;
std::atomic<uint64_t> SyncRes::s_nodelegated;
std::atomic<uint64_t> 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;
LOG(prefix<<qname<<": query handled by Lua"<<endl);
}
else {
- ednsmask=getEDNSSubnetMask(d_requestor, qname, *remoteIP, d_incomingECSFound ? d_incomingECS : boost::none);
+ ednsmask=getEDNSSubnetMask(d_requestor, qname, *remoteIP);
if(ednsmask) {
LOG(prefix<<qname<<": Adding EDNS Client Subnet Mask "<<ednsmask->toString()<<" to query"<<endl);
}
return -1;
}
+boost::optional<Netmask> SyncRes::getEDNSSubnetMask(const ComboAddress& local, const DNSName&dn, const ComboAddress& rem)
+{
+ boost::optional<Netmask> 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>(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<DNSRecord>& ret)
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;
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) const;
+ boost::optional<Netmask> getEDNSSubnetMask(const ComboAddress& local, const DNSName&dn, const ComboAddress& rem);
+
ostringstream d_trace;
shared_ptr<RecursorLua4> d_pdl;
boost::optional<const EDNSSubnetOpts&> d_incomingECS;
uint64_t* pleaseWipePacketCache(const DNSName& canon, bool subtree);
uint64_t* pleaseWipeAndCountNegCache(const DNSName& canon, bool subtree=false);
void doCarbonDump(void*);
-boost::optional<Netmask> getEDNSSubnetMask(const ComboAddress& local, const DNSName&dn, const ComboAddress& rem, boost::optional<const EDNSSubnetOpts&> incomingECS);
void parseEDNSSubnetWhitelist(const std::string& wlist);
extern __thread struct timeval g_now;