Skip to content

Commit 4a86f65

Browse files
authored
Merge pull request #15893 from omoerbeek/rec-rpz-custom-cname-chain
rec: try harder to follow cname chain on RPZ hit with custom CNAME record
2 parents 07be093 + 284d807 commit 4a86f65

File tree

2 files changed

+98
-3
lines changed

2 files changed

+98
-3
lines changed

pdns/recursordist/syncres.cc

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1900,14 +1900,20 @@ int SyncRes::doResolveNoQNameMinimization(const DNSName& qname, const QType qtyp
19001900
// we will get the records from the cache, resulting in a small overhead.
19011901
// This might be a real problem if we had a RPZ hit, though, because we do not want the processing to continue, since
19021902
// RPZ rules will not be evaluated anymore (we already matched).
1903-
const bool stoppedByPolicyHit = d_appliedPolicy.wasHit();
1904-
1903+
bool stoppedByPolicyHit = d_appliedPolicy.wasHit();
1904+
if (stoppedByPolicyHit && d_appliedPolicy.d_kind == DNSFilterEngine::PolicyKind::Custom && d_appliedPolicy.d_custom) {
1905+
// if the custom RPZ record was a CNAME we still need a full chase
1906+
// tested by unit test test_following_cname_chain_with_rpz
1907+
if (!d_appliedPolicy.d_custom->empty() && d_appliedPolicy.d_custom->at(0)->getType() == QType::CNAME) {
1908+
stoppedByPolicyHit = false;
1909+
}
1910+
}
19051911
if (fromCache != nullptr && (!d_cacheonly || stoppedByPolicyHit)) {
19061912
*fromCache = true;
19071913
}
19081914
/* Apply Post filtering policies */
19091915

1910-
if (d_wantsRPZ && !stoppedByPolicyHit) {
1916+
if (d_wantsRPZ && !d_appliedPolicy.wasHit()) {
19111917
auto luaLocal = g_luaconfs.getLocal();
19121918
if (luaLocal->dfe.getPostPolicy(ret, d_discardedPolicies, d_appliedPolicy)) {
19131919
mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());

pdns/recursordist/test-syncres_cc1.cc

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1568,6 +1568,95 @@ BOOST_AUTO_TEST_CASE(test_following_cname_chain_with_a)
15681568
BOOST_CHECK_EQUAL(ret[2].getContent()->getZoneRepresentation(), "192.0.2.2");
15691569
}
15701570

1571+
BOOST_AUTO_TEST_CASE(test_following_cname_chain_with_rpz)
1572+
{
1573+
std::unique_ptr<SyncRes> resolver;
1574+
initSR(resolver);
1575+
resolver->setQNameMinimization(true);
1576+
1577+
primeHints();
1578+
1579+
const DNSName target("rpzhit.powerdns.com.");
1580+
const DNSName cnameTargeta("cname-targeta.powerdns.com");
1581+
const DNSName cnameTargetb("cname-targetb.powerdns.com");
1582+
1583+
resolver->setAsyncCallback([&](const ComboAddress& address, const DNSName& domain, int /* type */, bool /* doTCP */, bool /* sendRDQuery */, int /* EDNS0Level */, struct timeval* /* now */, boost::optional<Netmask>& /* srcmask */, const ResolveContext& /* context */, LWResult* res, bool* /* chained */) {
1584+
if (isRootServer(address)) {
1585+
setLWResult(res, 0, false, false, true);
1586+
addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
1587+
addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
1588+
return LWResult::Result::Success;
1589+
}
1590+
if (address == ComboAddress("192.0.2.1:53")) {
1591+
1592+
if (domain == cnameTargeta) {
1593+
setLWResult(res, 0, true, false, false);
1594+
addRecordToLW(res, cnameTargeta, QType::CNAME, cnameTargetb.toString());
1595+
return LWResult::Result::Success;
1596+
}
1597+
if (domain == cnameTargetb) {
1598+
setLWResult(res, 0, true, false, false);
1599+
addRecordToLW(res, domain, QType::A, "192.0.2.2", DNSResourceRecord::ANSWER, 10);
1600+
}
1601+
1602+
return LWResult::Result::Success;
1603+
}
1604+
1605+
return LWResult::Result::Timeout;
1606+
});
1607+
1608+
DNSFilterEngine::Policy pol;
1609+
pol.d_ttl = 600;
1610+
pol.d_kind = DNSFilterEngine::PolicyKind::Custom;
1611+
auto customRecord = DNSRecordContent::make(QType::CNAME, QClass::IN, cnameTargeta.toString());
1612+
std::vector<std::shared_ptr<const DNSRecordContent>> custom = {customRecord};
1613+
pol.setCustom(custom);
1614+
std::shared_ptr<DNSFilterEngine::Zone> zone = std::make_shared<DNSFilterEngine::Zone>();
1615+
zone->setName("Unit test policy 0");
1616+
zone->addQNameTrigger(target, std::move(pol));
1617+
auto luaconfsCopy = g_luaconfs.getCopy();
1618+
luaconfsCopy.dfe.clearZones();
1619+
luaconfsCopy.dfe.addZone(zone);
1620+
g_luaconfs.setState(luaconfsCopy);
1621+
1622+
time_t now = time(nullptr);
1623+
1624+
vector<DNSRecord> ret;
1625+
int res = resolver->beginResolve(target, QType(QType::A), QClass::IN, ret);
1626+
BOOST_CHECK_EQUAL(res, RCode::NoError);
1627+
BOOST_REQUIRE_EQUAL(ret.size(), 3U);
1628+
BOOST_CHECK(ret[0].d_type == QType::CNAME);
1629+
BOOST_CHECK_EQUAL(ret[0].d_name, target);
1630+
BOOST_CHECK_EQUAL(ret[0].getContent()->getZoneRepresentation(), cnameTargeta.toString());
1631+
BOOST_CHECK(ret[1].d_type == QType::CNAME);
1632+
BOOST_CHECK_EQUAL(ret[1].d_name, cnameTargeta);
1633+
BOOST_CHECK_EQUAL(ret[1].getContent()->getZoneRepresentation(), cnameTargetb.toString());
1634+
BOOST_CHECK(ret[2].d_type == QType::A);
1635+
BOOST_CHECK_EQUAL(ret[2].d_name, cnameTargetb);
1636+
BOOST_CHECK_EQUAL(ret[2].getContent()->getZoneRepresentation(), "192.0.2.2");
1637+
1638+
// Let the final record expire. If an RPZ producing a custom CNAME was hit, we used to not follow
1639+
// the CNAME as aggressively as needed. The symptom being the final record missing from the
1640+
// result.
1641+
resolver->setNow(timeval{now + 20, 0});
1642+
resolver->setQNameMinimization(true); // XXX find out why this is needed
1643+
1644+
ret.clear();
1645+
resolver->d_appliedPolicy = DNSFilterEngine::Policy();
1646+
res = resolver->beginResolve(target, QType(QType::A), QClass::IN, ret);
1647+
BOOST_CHECK_EQUAL(res, RCode::NoError);
1648+
BOOST_REQUIRE_EQUAL(ret.size(), 3U);
1649+
BOOST_CHECK(ret[0].d_type == QType::CNAME);
1650+
BOOST_CHECK_EQUAL(ret[0].d_name, target);
1651+
BOOST_CHECK_EQUAL(ret[0].getContent()->getZoneRepresentation(), cnameTargeta.toString());
1652+
BOOST_CHECK(ret[1].d_type == QType::CNAME);
1653+
BOOST_CHECK_EQUAL(ret[1].d_name, cnameTargeta);
1654+
BOOST_CHECK_EQUAL(ret[1].getContent()->getZoneRepresentation(), cnameTargetb.toString());
1655+
BOOST_CHECK(ret[2].d_type == QType::A);
1656+
BOOST_CHECK_EQUAL(ret[2].d_name, cnameTargetb);
1657+
BOOST_CHECK_EQUAL(ret[2].getContent()->getZoneRepresentation(), "192.0.2.2");
1658+
}
1659+
15711660
BOOST_AUTO_TEST_CASE(test_cname_nxdomain)
15721661
{
15731662
std::unique_ptr<SyncRes> sr;

0 commit comments

Comments
 (0)