extra.clear();
}
+ std::string server = c.ds.toStringWithPort();
+ std::string protocol = dnsdist::Protocol(c.protocol).toString();
+ if (server == "0.0.0.0:0") {
+ server = "Cache";
+ protocol = "-";
+ }
if (c.usec != std::numeric_limits<decltype(c.usec)>::max()) {
- out.emplace(c.when, (fmt % DiffTime(now, c.when) % c.requestor.toStringWithPort() % dnsdist::Protocol(c.protocol).toString() % c.ds.toStringWithPort() % htons(c.dh.id) % c.name.toString() % qt.toString() % (c.usec / 1000.0) % (c.dh.tc ? "TC" : "") % (c.dh.rd ? "RD" : "") % (c.dh.aa ? "AA" : "") % (RCode::to_s(c.dh.rcode) + extra)).str());
+ out.emplace(c.when, (fmt % DiffTime(now, c.when) % c.requestor.toStringWithPort() % protocol % server % htons(c.dh.id) % c.name.toString() % qt.toString() % (c.usec / 1000.0) % (c.dh.tc ? "TC" : "") % (c.dh.rd ? "RD" : "") % (c.dh.aa ? "AA" : "") % (RCode::to_s(c.dh.rcode) + extra)).str());
}
else {
- out.emplace(c.when, (fmt % DiffTime(now, c.when) % c.requestor.toStringWithPort() % dnsdist::Protocol(c.protocol).toString() % c.ds.toStringWithPort() % htons(c.dh.id) % c.name.toString() % qt.toString() % "T.O" % (c.dh.tc ? "TC" : "") % (c.dh.rd ? "RD" : "") % (c.dh.aa ? "AA" : "") % (RCode::to_s(c.dh.rcode) + extra)).str());
+ out.emplace(c.when, (fmt % DiffTime(now, c.when) % c.requestor.toStringWithPort() % protocol % server % htons(c.dh.id) % c.name.toString() % qt.toString() % "T.O" % (c.dh.tc ? "TC" : "") % (c.dh.rd ? "RD" : "") % (c.dh.aa ? "AA" : "") % (RCode::to_s(c.dh.rcode) + extra)).str());
}
if (limit && *limit == ++num) {
backendProtocol = dnsdist::Protocol::DoTCP;
}
::handleResponseSent(ids, udiff, state->d_ci.remote, ds->d_config.remote, static_cast<unsigned int>(currentResponse.d_buffer.size()), currentResponse.d_cleartextDH, backendProtocol);
+ } else {
+ const auto& ids = currentResponse.d_idstate;
+ double udiff = ids.sentTime.udiff();
+ ::handleResponseSent(ids, udiff, state->d_ci.remote, ComboAddress(), static_cast<unsigned int>(currentResponse.d_buffer.size()), currentResponse.d_cleartextDH, ids.protocol);
}
}
TCPResponse response;
response.d_selfGenerated = true;
response.d_buffer = std::move(state->d_buffer);
+ setIDStateFromDNSQuestion(response.d_idstate, dq, std::move(qname));
+ response.d_idstate.origID = dh->id;
+ response.d_idstate.cs = state->d_ci.cs;
+
+ DNSResponse dr = makeDNSResponseFromIDState(response.d_idstate, response.d_buffer);
+ memcpy(&response.d_cleartextDH, dr.getHeader(), sizeof(response.d_cleartextDH));
+
state->d_state = IncomingTCPConnectionState::State::idle;
++state->d_currentQueriesCount;
state->queueResponse(state, now, std::move(response));
}
void handleResponseSent(const IDState& ids, double udiff, const ComboAddress& client, const ComboAddress& backend, unsigned int size, const dnsheader& cleartextDH, dnsdist::Protocol protocol)
+{
+ handleResponseSent(ids.qname, ids.qtype, udiff, client, backend, size, cleartextDH, protocol);
+}
+
+void handleResponseSent(const DNSName& qname, const QType& qtype, double udiff, const ComboAddress& client, const ComboAddress& backend, unsigned int size, const dnsheader& cleartextDH, dnsdist::Protocol protocol)
{
struct timespec ts;
gettime(&ts);
- g_rings.insertResponse(ts, client, ids.qname, ids.qtype, static_cast<unsigned int>(udiff), size, cleartextDH, backend, protocol);
+ g_rings.insertResponse(ts, client, qname, qtype, static_cast<unsigned int>(udiff), size, cleartextDH, backend, protocol);
switch (cleartextDH.rcode) {
case RCode::NXDomain:
#endif /* DISABLE_RECVMMSG */
/* we use dest, always, because we don't want to use the listening address to send a response since it could be 0.0.0.0 */
sendUDPResponse(cs.udpFD, query, dq.delayMsec, dest, remote);
+
+ handleResponseSent(qname, qtype, 0., remote, ComboAddress(), query.size(), *dh, dnsdist::Protocol::DoUDP);
return;
}
void setIDStateFromDNSQuestion(IDState& ids, DNSQuestion& dq, DNSName&& qname);
ssize_t udpClientSendRequestToBackend(const std::shared_ptr<DownstreamState>& ss, const int sd, const PacketBuffer& request, bool healthCheck = false);
+void handleResponseSent(const DNSName& qname, const QType& qtype, double udiff, const ComboAddress& client, const ComboAddress& backend, unsigned int size, const dnsheader& cleartextDH, dnsdist::Protocol protocol);
void handleResponseSent(const IDState& ids, double udiff, const ComboAddress& client, const ComboAddress& backend, unsigned int size, const dnsheader& cleartextDH, dnsdist::Protocol protocol);
if (du->response.empty()) {
du->response = std::move(du->query);
}
+ auto dh = const_cast<struct dnsheader*>(reinterpret_cast<const struct dnsheader*>(du->response.data()));
+
+ handleResponseSent(qname, QType(qtype), 0., du->downstream->d_config.remote, ComboAddress(), du->response.size(), *dh, du->downstream->getProtocol());
sendDoHUnitToTheMainThread(std::move(du), "DoH self-answered response");
return;
}
name = 'servfailrate.dynblocks.tests.powerdns.com.'
self.doTestRCodeRate(name, dns.rcode.SERVFAIL)
+class TestDynBlockServFailsCached(DynBlocksTest):
+
+ _dynBlockQPS = 10
+ _dynBlockPeriod = 2
+ _dynBlockDuration = 5
+ _config_params = ['_dynBlockQPS', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort']
+ _config_template = """
+ pc = newPacketCache(10000, {maxTTL=86400, minTTL=0, temporaryFailureTTL=60, staleTTL=60, dontAge=false})
+ getPool(""):setCache(pc)
+ function maintenance()
+ addDynBlocks(exceedServFails(%d, %d), "Exceeded servfail rate", %d)
+ end
+ newServer{address="127.0.0.1:%s"}
+ """
+
+ def testDynBlocksServFailRateCached(self):
+ """
+ Dyn Blocks: Make sure cache hit responses also gets inserted into rings
+ """
+ name = 'servfailrate.dynblocks.tests.powerdns.com.'
+ rcode = dns.rcode.SERVFAIL
+ query = dns.message.make_query(name, 'A', 'IN')
+ response = dns.message.make_response(query)
+ 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.set_rcode(rcode)
+
+
+ for method in ("sendUDPQuery", "sendTCPQuery"):
+ print(method, "()")
+ sender = getattr(self, method)
+
+ # fill the cache
+ (receivedQuery, receivedResponse) = sender(query, expectedResponse)
+ receivedQuery.id = query.id
+ self.assertEqual(query, receivedQuery)
+ self.assertEqual(expectedResponse, receivedResponse)
+
+ # wait for the maintenance function to run
+ time.sleep(2)
+
+ # we should NOT be dropped!
+ (_, receivedResponse) = sender(query, response=None)
+ self.assertEqual(receivedResponse, expectedResponse)
+
+ # now with rcode!
+ sent = 0
+ allowed = 0
+ for _ in range((self._dynBlockQPS * self._dynBlockPeriod) + 1):
+ (_, receivedResponse) = sender(query, expectedResponse)
+ sent = sent + 1
+ self.assertEqual(expectedResponse, receivedResponse)
+ allowed = allowed + 1
+ # we might be already blocked, but we should have been able to send
+ # at least self._dynBlockQPS queries
+ self.assertGreaterEqual(allowed, self._dynBlockQPS)
+
+ if allowed == sent:
+ # wait for the maintenance function to run
+ time.sleep(2)
+
+ # we should now be dropped for up to self._dynBlockDuration + self._dynBlockPeriod
+ (_, receivedResponse) = sender(query, response=None, useQueue=False)
+ self.assertEqual(receivedResponse, None)
+
+ # wait until we are not blocked anymore
+ time.sleep(self._dynBlockDuration + self._dynBlockPeriod)
+
+ # this one should succeed
+ (receivedQuery, receivedResponse) = sender(query, response=None)
+ self.assertEqual(expectedResponse, receivedResponse)
+
class TestDynBlockAllowlist(DynBlocksTest):
_dynBlockQPS = 10