* Number of entries in a given section (RecordsCountRule)
* Number of entries of a specific type in a given section (RecordsTypeCountRule)
* Presence of trailing data (TrailingDataRule)
+ * Number of labels in the qname (QNameLabelsCountRule)
+ * Wire length of the qname (QNameWireLengthRule)
Special rules are:
* an OpcodeRule
* an OrRule
* a QClassRule
+ * a QNameLabelsCountRule
+ * a QNameWireLengthRule
* a QTypeRule
* a RegexRule
* a RE2Rule
* `OrRule()`: matches if at least one of the sub-rules matches
* `OpcodeRule()`: matches queries with the specified opcode
* `QClassRule(qclass)`: matches queries with the specified qclass (numeric)
+ * `QNameLabelsCountRule(min, max)`: matches if the qname has less than `min` or more than `max` labels
+ * `QNameWireLengthRule(min, max)`: matches if the qname's length on the wire is less than `min` or more than `max` bytes
* `QTypeRule(qtype)`: matches queries with the specified qtype
* `RegexRule(regex)`: matches the query name against the supplied regex
* `RecordsCountRule(section, minCount, maxCount)`: matches if there is at least `minCount` and at most `maxCount` records in the `section` section
* `toStringWithPort()`: alias for `tostringWithPort()`
* DNSName related:
* `newDNSName(name)`: make a DNSName based on this .-terminated name
+ * member `countLabels()`: return the number of labels
* member `isPartOf(dnsname)`: is this dnsname part of that dnsname
* member `tostring()`: return as a human friendly . terminated string
* member `toString()`: alias for `tostring()`
+ * member `wirelength()`: return the length on the wire
* DNSQuestion related:
* member `dh`: DNSHeader
* member `len`: the question length
return std::shared_ptr<DNSRule>(new TrailingDataRule());
});
+ g_lua.writeFunction("QNameLabelsCountRule", [](unsigned int minLabelsCount, unsigned int maxLabelsCount) {
+ return std::shared_ptr<DNSRule>(new QNameLabelsCountRule(minLabelsCount, maxLabelsCount));
+ });
+
+ g_lua.writeFunction("QNameWireLengthRule", [](size_t min, size_t max) {
+ return std::shared_ptr<DNSRule>(new QNameWireLengthRule(min, max));
+ });
+
g_lua.writeFunction("addAction", [](luadnsrule_t var, std::shared_ptr<DNSAction> ea)
{
setLuaSideEffect();
g_lua.registerFunction("toStringWithPort", &ComboAddress::toStringWithPort);
g_lua.registerFunction<uint16_t(ComboAddress::*)()>("getPort", [](const ComboAddress& ca) { return ntohs(ca.sin4.sin_port); } );
g_lua.registerFunction("isPartOf", &DNSName::isPartOf);
+ g_lua.registerFunction("countLabels", &DNSName::countLabels);
+ g_lua.registerFunction("wirelength", &DNSName::wirelength);
g_lua.registerFunction<string(DNSName::*)()>("tostring", [](const DNSName&dn ) { return dn.toString(); });
g_lua.registerFunction<string(DNSName::*)()>("toString", [](const DNSName&dn ) { return dn.toString(); });
g_lua.writeFunction("newDNSName", [](const std::string& name) { return DNSName(name); });
(_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False)
self.assertEquals(receivedResponse, expectedResponse)
+class TestAdvancedLabelsCountRule(DNSDistTest):
+
+ _config_template = """
+ addAction(QNameLabelsCountRule(5,6), RCodeAction(dnsdist.REFUSED))
+ newServer{address="127.0.0.1:%s"}
+ """
+
+ def testAdvancedLabelsCountRule(self):
+ """
+ Advanced: QNameLabelsCountRule(5,6)
+ """
+ # 6 labels, we should be fine
+ name = 'ok.labelscount.advanced.tests.powerdns.com.'
+ query = dns.message.make_query(name, 'A', 'IN')
+ response = dns.message.make_response(query)
+ rrset = dns.rrset.from_text(name,
+ 3600,
+ dns.rdataclass.IN,
+ dns.rdatatype.A,
+ '192.0.2.1')
+ response.answer.append(rrset)
+
+ (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+ self.assertTrue(receivedQuery)
+ self.assertTrue(receivedResponse)
+ receivedQuery.id = query.id
+ self.assertEquals(query, receivedQuery)
+ self.assertEquals(response, receivedResponse)
+
+ (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
+ self.assertTrue(receivedQuery)
+ self.assertTrue(receivedResponse)
+ receivedQuery.id = query.id
+ self.assertEquals(query, receivedQuery)
+ self.assertEquals(response, receivedResponse)
+
+ # more than 6 labels, the query should be refused
+ name = 'not.ok.labelscount.advanced.tests.powerdns.com.'
+ query = dns.message.make_query(name, 'A', 'IN')
+ expectedResponse = dns.message.make_response(query)
+ expectedResponse.set_rcode(dns.rcode.REFUSED)
+
+ (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
+ self.assertEquals(receivedResponse, expectedResponse)
+
+ (_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False)
+ self.assertEquals(receivedResponse, expectedResponse)
+
+ # less than 5 labels, the query should be refused
+ name = 'labelscountadvanced.tests.powerdns.com.'
+ query = dns.message.make_query(name, 'A', 'IN')
+ expectedResponse = dns.message.make_response(query)
+ expectedResponse.set_rcode(dns.rcode.REFUSED)
+
+ (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
+ self.assertEquals(receivedResponse, expectedResponse)
+
+ (_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False)
+ self.assertEquals(receivedResponse, expectedResponse)
+
+class TestAdvancedWireLengthRule(DNSDistTest):
+
+ _config_template = """
+ addAction(QNameWireLengthRule(54,56), RCodeAction(dnsdist.REFUSED))
+ newServer{address="127.0.0.1:%s"}
+ """
+
+ def testAdvancedWireLengthRule(self):
+ """
+ Advanced: QNameWireLengthRule(54,56)
+ """
+ name = 'longenough.qnamewirelength.advanced.tests.powerdns.com.'
+ query = dns.message.make_query(name, 'A', 'IN')
+ response = dns.message.make_response(query)
+ rrset = dns.rrset.from_text(name,
+ 3600,
+ dns.rdataclass.IN,
+ dns.rdatatype.A,
+ '192.0.2.1')
+ response.answer.append(rrset)
+
+ (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+ self.assertTrue(receivedQuery)
+ self.assertTrue(receivedResponse)
+ receivedQuery.id = query.id
+ self.assertEquals(query, receivedQuery)
+ self.assertEquals(response, receivedResponse)
+
+ (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
+ self.assertTrue(receivedQuery)
+ self.assertTrue(receivedResponse)
+ receivedQuery.id = query.id
+ self.assertEquals(query, receivedQuery)
+ self.assertEquals(response, receivedResponse)
+
+ # too short, the query should be refused
+ name = 'short.qnamewirelength.advanced.tests.powerdns.com.'
+ query = dns.message.make_query(name, 'A', 'IN')
+ expectedResponse = dns.message.make_response(query)
+ expectedResponse.set_rcode(dns.rcode.REFUSED)
+
+ (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
+ self.assertEquals(receivedResponse, expectedResponse)
+
+ (_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False)
+ self.assertEquals(receivedResponse, expectedResponse)
+
+ # too long, the query should be refused
+ name = 'toolongtobevalid.qnamewirelength.advanced.tests.powerdns.com.'
+ query = dns.message.make_query(name, 'A', 'IN')
+ expectedResponse = dns.message.make_response(query)
+ expectedResponse.set_rcode(dns.rcode.REFUSED)
+
+ (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
+ self.assertEquals(receivedResponse, expectedResponse)
+
+ (_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False)
+ self.assertEquals(receivedResponse, expectedResponse)