DNSAction::Action operator()(DNSQuestion* dnsquestion, std::string* ruleresult) const override
{
(void)ruleresult;
+ dnsdist::self_answers::removeRecordsAndSetRCode(*dnsquestion, d_rcode);
dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsquestion->getMutableData(), [this](dnsheader& header) {
- header.rcode = d_rcode;
- header.qr = true; // for good measure
setResponseHeadersFromConfig(header, d_responseConfig);
return true;
});
DNSAction::Action operator()(DNSQuestion* dnsquestion, std::string* ruleresult) const override
{
(void)ruleresult;
+ dnsdist::self_answers::removeRecordsAndSetRCode(*dnsquestion, (d_rcode & 0xF));
dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsquestion->getMutableData(), [this](dnsheader& header) {
- header.rcode = (d_rcode & 0xF);
- header.qr = true; // for good measure
setResponseHeadersFromConfig(header, d_responseConfig);
return true;
});
return true;
}
+bool removeRecordsAndSetRCode(DNSQuestion& dnsQuestion, uint16_t rcode)
+{
+ bool dnssecOK = false;
+ bool hadEDNS = false;
+ if (dnsdist::configuration::getCurrentRuntimeConfiguration().d_addEDNSToSelfGeneratedResponses && queryHasEDNS(dnsQuestion)) {
+ hadEDNS = true;
+ dnssecOK = ((dnsdist::getEDNSZ(dnsQuestion) & EDNS_HEADER_FLAG_DO) != 0);
+ }
+
+ dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsQuestion.getMutableData(), [rcode](dnsheader& header) {
+ header.rcode = rcode;
+ header.qr = true;
+ header.qdcount = htons(1);
+ header.arcount = 0;
+ header.nscount = 0;
+ header.ancount = 0;
+ return true;
+ });
+ auto qnameWireLength = dnsQuestion.ids.qname.wirelength();
+ dnsQuestion.getMutableData().resize(sizeof(dnsheader) + qnameWireLength + 4);
+
+ if (hadEDNS) {
+ addEDNS(dnsQuestion.getMutableData(), dnsQuestion.getMaximumSize(), dnssecOK, dnsdist::configuration::getCurrentRuntimeConfiguration().d_payloadSizeSelfGenAnswers, 0);
+ }
+
+ return true;
+}
+
}
bool generateAnswerFromIPAddresses(DNSQuestion& dnsQuestion, const std::vector<ComboAddress>& addresses, const ResponseConfig& responseConfig);
bool generateAnswerFromRDataEntries(DNSQuestion& dnsQuestion, const std::vector<std::string>& entries, std::optional<uint16_t> typeForAny, const ResponseConfig& responseConfig);
bool generateAnswerFromRawPacket(DNSQuestion& dnsQuestion, const PacketBuffer& packet);
+bool removeRecordsAndSetRCode(DNSQuestion& dnsQuestion, uint16_t rcode);
}
}
auto setRCode = [&dnsQuestion](uint8_t rcode) {
- dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsQuestion.getMutableData(), [rcode](dnsheader& header) {
- header.rcode = rcode;
- header.qr = true;
- return true;
- });
+ dnsdist::self_answers::removeRecordsAndSetRCode(dnsQuestion, rcode);
};
switch (action) {
#ifndef DISABLE_DYNBLOCKS
const auto defaultDynBlockAction = dnsdist::configuration::getCurrentRuntimeConfiguration().d_dynBlockAction;
auto setRCode = [&dnsQuestion](uint8_t rcode) {
- dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsQuestion.getMutableData(), [rcode](dnsheader& header) {
- header.rcode = rcode;
- header.qr = true;
- return true;
- });
+ dnsdist::self_answers::removeRecordsAndSetRCode(dnsQuestion, rcode);
};
/* the Dynamic Block mechanism supports address and port ranges, so we need to pass the full address and port */
vinfolog("%s query for %s|%s from %s, no downstream server available", servFailOnNoPolicy ? "ServFailed" : "Dropped", dnsQuestion.ids.qname.toLogString(), QType(dnsQuestion.ids.qtype).toString(), dnsQuestion.ids.origRemote.toStringWithPort());
if (servFailOnNoPolicy) {
- dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsQuestion.getMutableData(), [](dnsheader& header) {
- header.rcode = RCode::ServFail;
- header.qr = true;
- return true;
- });
+ dnsdist::self_answers::removeRecordsAndSetRCode(dnsQuestion, RCode::ServFail);
fixUpQueryTurnedResponse(dnsQuestion, dnsQuestion.ids.origFlags);
gettime(&now);
if ((dnsQuestion.ids.qtype == QType::AXFR || dnsQuestion.ids.qtype == QType::IXFR) && (dnsQuestion.getProtocol() == dnsdist::Protocol::DoH || dnsQuestion.getProtocol() == dnsdist::Protocol::DoQ || dnsQuestion.getProtocol() == dnsdist::Protocol::DoH3)) {
- dnsQuestion.editHeader([](dnsheader& header) {
- header.rcode = RCode::NotImp;
- header.qr = true;
- return true;
- });
+ dnsdist::self_answers::removeRecordsAndSetRCode(dnsQuestion, RCode::NotImp);
return processQueryAfterRules(dnsQuestion, selectedBackend);
}
The ``options`` parameter of :func:`HTTPStatusAction` has been deprecated because it had unexpected side-effects, and should thus no longer be used.
+In some cases, :program:`dnsdist` turns an incoming into a response, setting the response code in the process. When doing so, it was not properly cleaning up records present in the answer, authority or additional sections, which could have been surprising to clients and wasted bandwidth. This has now been fixed. The cases in question are:
+
+* :func:`RCodeAction`
+* :func:`ERCodeAction`
+* returning ``DNSAction.Nxdomain``, ``DNSAction.Refused`` or ``DNSAction.ServFail`` from ``Lua``
+* using the ``DNSAction.Nxdomain``, ``DNSAction.Refused`` or ``DNSAction.ServFail`` dynamic block actions
+* sending ``Server Failure`` when no downstream servers are usable
+* receiving a zone transfer request over DoQ, DoH or DoH3
+
1.8.x to 1.9.0
--------------
'127.0.0.1'))
expectedResponse = dns.message.make_response(query)
expectedResponse.set_rcode(dns.rcode.REFUSED)
- # this is not great, we should fix that!
- expectedResponse.additional.append(dns.rrset.from_text(name,
- 3600,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '127.0.0.1'))
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
expectedResponse = dns.message.make_response(query)
expectedResponse.set_rcode(dns.rcode.REFUSED)
- expectedResponse.answer.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)
query.flags &= ~dns.flags.RD
expectedResponse = dns.message.make_response(query)
expectedResponse.set_rcode(dns.rcode.REFUSED)
- expectedResponse.authority.append(rrset)
for method in ("sendUDPQuery", "sendTCPQuery"):
sender = getattr(self, method)