*flags |= origFlags;
}
-static uint16_t getRDAndCDFlagsFromDNSHeader(const struct dnsheader* dh)
-{
- return static_cast<uint16_t>(dh->rd) << FLAGS_RD_OFFSET | static_cast<uint16_t>(dh->cd) << FLAGS_CD_OFFSET;
-}
-
static bool fixUpQueryTurnedResponse(DNSQuestion& dq, const uint16_t origFlags)
{
restoreFlags(dq.getHeader(), origFlags);
return false;
}
- /* We need to get the flags for the packet cache before restoring the original ones, otherwise it might not match later queries */
- const auto cacheFlags = getRDAndCDFlagsFromDNSHeader(dr.getHeader());
-
bool zeroScope = false;
if (!fixUpResponse(response, *dr.qname, dr.origFlags, dr.ednsAdded, dr.ecsAdded, dr.useZeroScope ? &zeroScope : nullptr)) {
return false;
zeroScope = false;
}
// if zeroScope, pass the pre-ECS hash-key and do not pass the subnet to the cache
- dr.packetCache->insert(zeroScope ? dr.cacheKeyNoECS : dr.cacheKey, zeroScope ? boost::none : dr.subnet, cacheFlags, dr.dnssecOK, *dr.qname, dr.qtype, dr.qclass, response, receivedOverUDP, dr.getHeader()->rcode, dr.tempFailureTTL);
+ dr.packetCache->insert(zeroScope ? dr.cacheKeyNoECS : dr.cacheKey, zeroScope ? boost::none : dr.subnet, dr.cacheFlags, dr.dnssecOK, *dr.qname, dr.qtype, dr.qclass, response, receivedOverUDP, dr.getHeader()->rcode, dr.tempFailureTTL);
}
#ifdef HAVE_DNSCRYPT
return ProcessQueryResult::Drop;
}
+ /* save the DNS flags as sent to the backend so we can cache the answer with the right flags later */
+ dq.cacheFlags = *getFlagsFromDNSHeader(dq.getHeader());
+
if (dq.addXPF && selectedBackend->xpfRRCode != 0) {
addXPF(dq, selectedBackend->xpfRRCode);
}
const uint16_t qclass;
uint16_t ecsPrefixLength;
uint16_t origFlags;
+ uint16_t cacheFlags; /* DNS flags as sent to the backend */
const Protocol protocol;
uint8_t ednsRCode{0};
bool skipCache{false};
{
IDState(): sentTime(true), tempFailureTTL(boost::none) { origDest.sin4.sin_family = 0;}
IDState(const IDState& orig) = delete;
- IDState(IDState&& rhs): subnet(rhs.subnet), origRemote(rhs.origRemote), origDest(rhs.origDest), hopRemote(rhs.hopRemote), hopLocal(rhs.hopLocal), qname(std::move(rhs.qname)), sentTime(rhs.sentTime), dnsCryptQuery(std::move(rhs.dnsCryptQuery)), packetCache(std::move(rhs.packetCache)), qTag(std::move(rhs.qTag)), tempFailureTTL(rhs.tempFailureTTL), cs(rhs.cs), du(std::move(rhs.du)), cacheKey(rhs.cacheKey), cacheKeyNoECS(rhs.cacheKeyNoECS), origFD(rhs.origFD), delayMsec(rhs.delayMsec), qtype(rhs.qtype), qclass(rhs.qclass), origID(rhs.origID), origFlags(rhs.origFlags), protocol(rhs.protocol), ednsAdded(rhs.ednsAdded), ecsAdded(rhs.ecsAdded), skipCache(rhs.skipCache), destHarvested(rhs.destHarvested), dnssecOK(rhs.dnssecOK), useZeroScope(rhs.useZeroScope)
+ IDState(IDState&& rhs): subnet(rhs.subnet), origRemote(rhs.origRemote), origDest(rhs.origDest), hopRemote(rhs.hopRemote), hopLocal(rhs.hopLocal), qname(std::move(rhs.qname)), sentTime(rhs.sentTime), dnsCryptQuery(std::move(rhs.dnsCryptQuery)), packetCache(std::move(rhs.packetCache)), qTag(std::move(rhs.qTag)), tempFailureTTL(rhs.tempFailureTTL), cs(rhs.cs), du(std::move(rhs.du)), cacheKey(rhs.cacheKey), cacheKeyNoECS(rhs.cacheKeyNoECS), origFD(rhs.origFD), delayMsec(rhs.delayMsec), qtype(rhs.qtype), qclass(rhs.qclass), origID(rhs.origID), origFlags(rhs.origFlags), cacheFlags(rhs.cacheFlags), protocol(rhs.protocol), ednsAdded(rhs.ednsAdded), ecsAdded(rhs.ecsAdded), skipCache(rhs.skipCache), destHarvested(rhs.destHarvested), dnssecOK(rhs.dnssecOK), useZeroScope(rhs.useZeroScope)
{
if (rhs.isInUse()) {
throw std::runtime_error("Trying to move an in-use IDState");
qclass = rhs.qclass;
origID = rhs.origID;
origFlags = rhs.origFlags;
+ cacheFlags = rhs.cacheFlags;
protocol = rhs.protocol;
uniqueId = std::move(rhs.uniqueId);
ednsAdded = rhs.ednsAdded;
uint16_t qclass{0}; // 2
uint16_t origID{0}; // 2
uint16_t origFlags{0}; // 2
+ uint16_t cacheFlags{0}; // DNS flags as sent to the backend // 2
DNSQuestion::Protocol protocol; // 1
boost::optional<boost::uuids::uuid> uniqueId{boost::none}; // 17 (placed here to reduce the space lost to padding)
bool ednsAdded{false};
'192.0.2.1')
expectedResponse.answer.append(rrset)
- (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
- self.assertTrue(receivedQuery)
- self.assertTrue(receivedResponse)
- receivedQuery.id = expectedQuery.id
- self.assertEqual(expectedQuery, receivedQuery)
- self.assertEqual(receivedResponse, expectedResponse)
+ for method in ("sendUDPQuery", "sendTCPQuery"):
+ sender = getattr(self, method)
+ (receivedQuery, receivedResponse) = sender(query, response)
+ self.assertTrue(receivedQuery)
+ self.assertTrue(receivedResponse)
+ receivedQuery.id = expectedQuery.id
+ self.assertEqual(expectedQuery, receivedQuery)
+ self.assertEqual(receivedResponse, expectedResponse)
# next query should hit the cache
- (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
- self.assertFalse(receivedQuery)
- self.assertTrue(receivedResponse)
- self.assertEqual(receivedResponse, expectedResponse)
+ for method in ("sendUDPQuery", "sendTCPQuery"):
+ sender = getattr(self, method)
+ (receivedQuery, receivedResponse) = sender(query, response=None, useQueue=False)
# same query with RD=0, should hit the cache as well
query.flags &= ~dns.flags.RD
dns.rdatatype.A,
'192.0.2.1')
expectedResponse.answer.append(rrset)
- (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
- self.assertFalse(receivedQuery)
- self.assertTrue(receivedResponse)
- self.assertEqual(receivedResponse, expectedResponse)
+ for method in ("sendUDPQuery", "sendTCPQuery"):
+ sender = getattr(self, method)
+ (receivedQuery, receivedResponse) = sender(query, response=None, useQueue=False)
+ self.assertFalse(receivedQuery)
+ self.assertTrue(receivedResponse)
+ self.assertEqual(receivedResponse, expectedResponse)
+
+class TestCachingBackendSettingRD(DNSDistTest):
+
+ _config_template = """
+ pc = newPacketCache(100)
+ getPool(""):setCache(pc)
+ newServer{address="127.0.0.1:%d"}
+ """
+
+ def testCachingBackendSetRD(self):
+ """
+ Cache: The backend sets RD=1 in the response even if the query had RD=0
+ """
+ name = 'backend-sets-rd.tests.powerdns.com.'
+ query = dns.message.make_query(name, 'A', 'IN')
+ query.flags &= ~dns.flags.RD
+ expectedQuery = dns.message.make_query(name, 'A', 'IN')
+ expectedQuery.flags &= ~dns.flags.RD
+ response = dns.message.make_response(query)
+ response.flags |= dns.flags.RD
+ rrset = dns.rrset.from_text(name,
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.A,
+ '192.0.2.1')
+ response.answer.append(rrset)
+
+ expectedResponse = dns.message.make_response(query)
+ expectedResponse.flags &= ~dns.flags.RD
+ rrset = dns.rrset.from_text(name,
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.A,
+ '192.0.2.1')
+ expectedResponse.answer.append(rrset)
+
+ for method in ("sendUDPQuery", "sendTCPQuery"):
+ sender = getattr(self, method)
+ (receivedQuery, receivedResponse) = sender(query, response)
+ self.assertTrue(receivedQuery)
+ self.assertTrue(receivedResponse)
+ receivedQuery.id = expectedQuery.id
+ self.assertEqual(expectedQuery, receivedQuery)
+ self.assertEqual(receivedResponse, expectedResponse)
+
+ # exact same query should be cached
+ for method in ("sendUDPQuery", "sendTCPQuery"):
+ sender = getattr(self, method)
+ (receivedQuery, receivedResponse) = sender(query, response=None, useQueue=False)
+ self.assertFalse(receivedQuery)
+ self.assertTrue(receivedResponse)
+ self.assertEqual(receivedResponse, expectedResponse)
+
+ # same query with RD=1, should NOT hit the cache
+ query.flags |= dns.flags.RD
+ expectedResponse = dns.message.make_response(query)
+ rrset = dns.rrset.from_text(name,
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.A,
+ '192.0.2.1')
+ expectedResponse.answer.append(rrset)
+
+ for method in ("sendUDPQuery", "sendTCPQuery"):
+ sender = getattr(self, method)
+ (receivedQuery, receivedResponse) = sender(query, response)
+ self.assertTrue(receivedQuery)
+ self.assertTrue(receivedResponse)
+ self.assertEqual(receivedResponse, expectedResponse)