("Duplicate key name")
("Algorithm not supported")
("Bad Truncation")
+ ("Bad/missing Server Cookie")
;
std::string RCode::to_s(unsigned short rcode) {
return RCode::rcodes_s[rcode];
}
+std::string ERCode::to_s(unsigned short rcode) {
+ return RCode::to_s(rcode);
+}
+
class BoundsCheckingPointer
{
public:
#include "dnsname.hh"
#include <time.h>
#include <sys/types.h>
+
+#undef BADSIG // signal.h SIG_ERR
+
class DNSBackend;
struct DNSRecord;
static std::vector<std::string> rcodes_s;
};
+class ERCode
+{
+public:
+ enum rcodes_ { BADVERS=16, BADSIG=16, BADKEY=17, BADTIME=18, BADMODE=19, BADNAME=20, BADALG=21, BADTRUNC=22, BADCOOKIE=23 };
+ static std::string to_s(unsigned short rcode);
+};
+
class Opcode
{
public:
{ "QNameWireLengthRule", true, "min, max", "matches if the qname's length on the wire is less than `min` or more than `max` bytes" },
{ "QTypeRule", true, "qtype", "matches queries with the specified qtype" },
{ "RCodeRule", true, "rcode", "matches responses with the specified rcode" },
+ { "ERCodeRule", true, "rcode", "matches responses with the specified extended rcode (EDNS0)" },
{ "sendCustomTrap", true, "str", "send a custom `SNMP` trap from Lua, containing the `str` string"},
{ "setACL", true, "{netmask, netmask}", "replace the ACL set with these netmasks. Use `setACL({})` to reset the list, meaning no one can use us" },
{ "setAPIWritable", true, "bool, dir", "allow modifications via the API. if `dir` is set, it must be a valid directory where the configuration files will be written by the API" },
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "dnsdist.hh"
+#include "dnsdist-ecs.hh"
#include "dnsdist-lua.hh"
#include "dnsparser.hh"
int d_rcode;
};
+class ERCodeRule : public DNSRule
+{
+public:
+ ERCodeRule(int rcode) : d_rcode(rcode & 0xF), d_extrcode(rcode >> 4)
+ {
+ }
+ bool matches(const DNSQuestion* dq) const override
+ {
+ // avoid parsing EDNS OPT RR when not needed.
+ if (d_rcode != dq->dh->rcode) {
+ return false;
+ }
+
+ char * optStart = NULL;
+ size_t optLen = 0;
+ bool last = false;
+ int res = locateEDNSOptRR((char*)dq->dh, dq->len, &optStart, &optLen, &last);
+ if (res != 0) {
+ // no EDNS OPT RR
+ return d_extrcode == 0;
+ }
+
+ /* root label (1), type (2), class (2), ttl (4) + rdlen (2)*/
+ if (optLen < 11) {
+ return false;
+ }
+
+ if (*optStart != 0) {
+ // OPT RR Name != '.'
+ return false;
+ }
+ EDNS0Record edns0;
+ static_assert(sizeof(EDNS0Record) == sizeof(uint32_t), "sizeof(EDNS0Record) must match sizeof(uint32_t) AKA RR TTL size");
+ memcpy(&edns0, optStart + 5, sizeof edns0);
+
+ return d_extrcode == edns0.extRCode;
+ }
+ string toString() const override
+ {
+ return "ercode=="+ERCode::to_s(d_rcode | (d_extrcode << 4));
+ }
+private:
+ int d_rcode; // plain DNS Rcode
+ int d_extrcode; // upper bits in EDNS0 record
+};
+
class RDRule : public DNSRule
{
public:
return std::shared_ptr<DNSRule>(new RCodeRule(rcode));
});
+ g_lua.writeFunction("ERCodeRule", [](int rcode) {
+ return std::shared_ptr<DNSRule>(new ERCodeRule(rcode));
+ });
+
g_lua.writeFunction("showRules", []() {
setLuaNoSideEffect();
boost::format fmt("%-3d %9d %-50s %s\n");
{"YXRRSET", RCode::YXRRSet },
{"NXRRSET", RCode::NXRRSet },
{"NOTAUTH", RCode::NotAuth },
- {"NOTZONE", RCode::NotZone }
+ {"NOTZONE", RCode::NotZone },
+ {"BADVERS", ERCode::BADVERS },
+ {"BADSIG", ERCode::BADSIG },
+ {"BADKEY", ERCode::BADKEY },
+ {"BADTIME", ERCode::BADTIME },
+ {"BADMODE", ERCode::BADMODE },
+ {"BADNAME", ERCode::BADNAME },
+ {"BADALG", ERCode::BADALG },
+ {"BADTRUNC", ERCode::BADTRUNC },
+ {"BADCOOKIE",ERCode::BADCOOKIE },
};
vector<pair<string, int> > dd;
for(const auto& n : QType::names)
- ``DNSOpcode.Notify``
- ``DNSOpcode.Update``
+Reference: https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-5
+
.. _DNSQClass:
QClass
- ``QClass.NONE``
- ``QClass.ANY``
+Reference: https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-2
+
.. _DNSRCode:
RCode
- ``dnsdist.NXRRSET``
- ``dnsdist.NOTAUTH``
- ``dnsdist.NOTZONE``
+- ``dnsdist.BADVERS``
+- ``dnsdist.BADSIG``
+- ``dnsdist.BADKEY``
+- ``dnsdist.BADTIME``
+- ``dnsdist.BADMODE``
+- ``dnsdist.BADNAME``
+- ``dnsdist.BADALG``
+- ``dnsdist.BADTRUNC``
+- ``dnsdist.BADCOOKIE``
+
+RCodes below and including ``BADVERS`` are extended RCodes that can only be matched using :func:`ERCodeRule`.
+
+Reference: https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-6
.. _DNSSection:
.. function:: RCodeRule(rcode)
- Matches queries or responses the specified ``rcode``.
- ``rcode`` can be specified as an integer or as one of the built-in `RCode <DNSRcode>`.
+ Matches queries or responses with the specified ``rcode``.
+ ``rcode`` can be specified as an integer or as one of the built-in :ref:`DNSRCode`.
+ Only the non-extended RCode is matched (lower 4bits).
+
+ :param int rcode: The RCODE to match on
+
+.. function:: ERCodeRule(rcode)
+
+ Matches queries or responses with the specified ``rcode``.
+ ``rcode`` can be specified as an integer or as one of the built-in :ref:`DNSRCode`.
+ The full 16bit RCode will be matched. If no EDNS OPT RR is present, the upper 12 bits are treated as 0.
:param int rcode: The RCODE to match on
.. function:: RCodeAction(rcode)
Reply immediatly by turning the query into a response with the specified ``rcode``.
- ``rcode`` can be specified as an integer or as one of the built-in `RCode <#rcode>`_.
+ ``rcode`` can be specified as an integer or as one of the built-in :ref:`DNSRCode`.
:param int rcode: The RCODE to respond with.
import clientsubnetoption
import cookiesoption
from dnsdisttests import DNSDistTest
+from datetime import datetime, timedelta
class TestEdnsClientSubnetNoOverride(DNSDistTest):
"""
self.assertEquals(response, receivedResponse)
self.assertTrue((end - begin) < timedelta(0, 1))
+class TestResponseRuleERCode(DNSDistTest):
+
+ _config_template = """
+ newServer{address="127.0.0.1:%s"}
+ addResponseAction(ERCodeRule(dnsdist.BADVERS), DelayResponseAction(1000))
+ """
+
+ def testBADVERSDelayed(self):
+ """
+ Responses: Delayed on BADVERS
+
+ Send an A query to "delayed.responses.tests.powerdns.com.",
+ check that the response delay is longer than 1000 ms
+ for a BADVERS response over UDP, shorter for BADKEY and NoError.
+ """
+ name = 'delayed.responses.tests.powerdns.com.'
+ query = dns.message.make_query(name, 'A', 'IN')
+ response = dns.message.make_response(query)
+ response.use_edns(edns=True)
+
+ # BADVERS over UDP
+ # BADVERS == 16, so rcode==0, ercode==1
+ response.set_rcode(dns.rcode.BADVERS)
+ begin = datetime.now()
+ (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+ end = datetime.now()
+ receivedQuery.id = query.id
+ self.assertEquals(query, receivedQuery)
+ self.assertEquals(response, receivedResponse)
+ self.assertTrue((end - begin) > timedelta(0, 1))
+
+ # BADKEY (17, an ERCode) over UDP
+ response.set_rcode(17)
+ begin = datetime.now()
+ (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+ end = datetime.now()
+ receivedQuery.id = query.id
+ self.assertEquals(query, receivedQuery)
+ self.assertEquals(response, receivedResponse)
+ self.assertTrue((end - begin) < timedelta(0, 1))
+
+ # NoError (non-ERcode, basic RCode bits match BADVERS) over UDP
+ response.set_rcode(dns.rcode.NOERROR)
+ begin = datetime.now()
+ (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+ end = datetime.now()
+ receivedQuery.id = query.id
+ self.assertEquals(query, receivedQuery)
+ self.assertEquals(response, receivedResponse)
+ self.assertTrue((end - begin) < timedelta(0, 1))
+
class TestResponseRuleQNameDropped(DNSDistTest):
_config_template = """