{ "addLuaResponseAction", true, "x, func [, {uuid=\"UUID\"}]", "where 'x' is all the combinations from `addAction`, and func is a function with the parameter `dr`, which returns an action to be taken on this response packet. Good for rare packets but where you want to do a lot of processing" },
{ "addCacheHitResponseAction", true, "DNS rule, DNS response action [, {uuid=\"UUID\"}]", "add a cache hit response rule" },
{ "addResponseAction", true, "DNS rule, DNS response action [, {uuid=\"UUID\"}]", "add a response rule" },
+ { "addSelfAnsweredResponseAction", true, "DNS rule, DNS response action [, {uuid=\"UUID\"}]", "add a self-answered response rule" },
{ "addTLSLocal", true, "addr, certFile, keyFile[,params]", "listen to incoming DNS over TLS queries on the specified address using the specified certificate and key. The last parameter is a table" },
{ "AllowAction", true, "", "let these packets go through" },
{ "AllowResponseAction", true, "", "let these packets go through" },
{ "mvCacheHitResponseRule", true, "from, to", "move cache hit response rule 'from' to a position where it is in front of 'to'. 'to' can be one larger than the largest rule" },
{ "mvResponseRule", true, "from, to", "move response rule 'from' to a position where it is in front of 'to'. 'to' can be one larger than the largest rule" },
{ "mvRule", true, "from, to", "move rule 'from' to a position where it is in front of 'to'. 'to' can be one larger than the largest rule, in which case the rule will be moved to the last position" },
+ { "mvSelfAnsweredResponseRule", true, "from, to", "move self-answered response rule 'from' to a position where it is in front of 'to'. 'to' can be one larger than the largest rule" },
{ "newDNSName", true, "name", "make a DNSName based on this .-terminated name" },
{ "newPacketCache", true, "maxEntries[, maxTTL=86400, minTTL=0, temporaryFailureTTL=60, staleTTL=60, dontAge=false, numberOfShards=1, deferrableInsertLock=true]", "return a new Packet Cache" },
{ "newQPSLimiter", true, "rate, burst", "configure a QPS limiter with that rate and that burst capacity" },
{ "rmCacheHitResponseRule", true, "id", "remove cache hit response rule in position 'id', or whose uuid matches if 'id' is an UUID string" },
{ "rmResponseRule", true, "id", "remove response rule in position 'id', or whose uuid matches if 'id' is an UUID string" },
{ "rmRule", true, "id", "remove rule in position 'id', or whose uuid matches if 'id' is an UUID string" },
+ { "rmSelfAnsweredResponseRule", true, "id", "remove self-answered response rule in position 'id', or whose uuid matches if 'id' is an UUID string" },
{ "rmServer", true, "n", "remove server with index n" },
{ "roundrobin", false, "", "Simple round robin over available servers" },
{ "QNameLabelsCountRule", true, "min, max", "matches if the qname has less than `min` or more than `max` labels" },
{ "showResponseLatency", true, "", "show a plot of the response time latency distribution" },
{ "showResponseRules", true, "[showUUIDs]", "show all defined response rules, optionally with their UUIDs" },
{ "showRules", true, "[showUUIDs]", "show all defined rules, optionally with their UUIDs" },
+ { "showSelfAnsweredResponseRules", true, "[showUUIDs]", "show all defined self-answered response rules, optionally with their UUIDs" },
{ "showServerPolicy", true, "", "show name of currently operational server selection policy" },
{ "showServers", true, "", "output all servers" },
{ "showTCPStats", true, "", "show some statistics regarding TCP" },
{ "topClients", true, "n", "show top-`n` clients sending the most queries over length of ringbuffer" },
{ "topQueries", true, "n[, labels]", "show top 'n' queries, as grouped when optionally cut down to 'labels' labels" },
{ "topResponses", true, "n, kind[, labels]", "show top 'n' responses with RCODE=kind (0=NO Error, 2=ServFail, 3=ServFail), as grouped when optionally cut down to 'labels' labels" },
+ { "topCacheHitResponseRule", true, "", "move the last cache hit response rule to the first position" },
{ "topResponseRule", true, "", "move the last response rule to the first position" },
{ "topRule", true, "", "move the last rule to the first position" },
+ { "topSelfAnsweredResponseRule", true, "", "move the last self-answered response rule to the first position" },
{ "topSlow", true, "[top][, limit][, labels]", "show `top` queries slower than `limit` milliseconds, grouped by last `labels` labels" },
{ "truncateTC", true, "bool", "if set (defaults to no starting with dnsdist 1.2.0) truncate TC=1 answers so they are actually empty. Fixes an issue for PowerDNS Authoritative Server 2.9.22. Note: turning this on breaks compatibility with RFC 6891." },
{ "unregisterDynBPFFilter", true, "DynBPFFilter", "unregister this dynamic BPF filter" },
addAction(&g_cachehitresprulactions, var, boost::get<std::shared_ptr<DNSResponseAction> >(era), params);
});
+ g_lua.writeFunction("addSelfAnsweredResponseAction", [](luadnsrule_t var, boost::variant<std::shared_ptr<DNSAction>, std::shared_ptr<DNSResponseAction>> era, boost::optional<luaruleparams_t> params) {
+ if (era.type() != typeid(std::shared_ptr<DNSResponseAction>)) {
+ throw std::runtime_error("addSelfAnsweredResponseAction() can only be called with response-related actions, not query-related ones. Are you looking for addAction()?");
+ }
+
+ addAction(&g_selfansweredresprulactions, var, boost::get<std::shared_ptr<DNSResponseAction> >(era), params);
+ });
+
g_lua.registerFunction<void(DNSAction::*)()>("printStats", [](const DNSAction& ta) {
setLuaNoSideEffect();
auto stats = ta.getStats();
mvRule(&g_cachehitresprulactions, from, to);
});
+ g_lua.writeFunction("showSelfAnsweredResponseRules", [](boost::optional<bool> showUUIDs) {
+ showRules(&g_selfansweredresprulactions, showUUIDs);
+ });
+
+ g_lua.writeFunction("rmSelfAnsweredResponseRule", [](boost::variant<unsigned int, std::string> id) {
+ rmRule(&g_selfansweredresprulactions, id);
+ });
+
+ g_lua.writeFunction("topSelfAnsweredResponseRule", []() {
+ topRule(&g_selfansweredresprulactions);
+ });
+
+ g_lua.writeFunction("mvSelfAnsweredResponseRule", [](unsigned int from, unsigned int to) {
+ mvRule(&g_selfansweredresprulactions, from, to);
+ });
+
g_lua.writeFunction("rmRule", [](boost::variant<unsigned int, std::string> id) {
rmRule(&g_rulactions, id);
});
if(dq.dh->qr) { // something turned it into a response
restoreFlags(dh, origFlags);
+
+ DNSResponse dr(dq.qname, dq.qtype, dq.qclass, dq.local, dq.remote, reinterpret_cast<dnsheader*>(query), dq.size, dq.len, true, &queryRealTime);
+#ifdef HAVE_PROTOBUF
+ dr.uniqueId = dq.uniqueId;
+#endif
+ dr.qTag = dq.qTag;
+
+ if (!processResponse(holders.selfAnsweredRespRulactions, dr, &delayMsec)) {
+ goto drop;
+ }
+
#ifdef HAVE_DNSCRYPT
if (!encryptResponse(query, &dq.len, dq.size, true, dnsCryptQuery, nullptr, nullptr)) {
goto drop;
dq.dh->rcode = RCode::ServFail;
dq.dh->qr = true;
+ DNSResponse dr(dq.qname, dq.qtype, dq.qclass, dq.local, dq.remote, reinterpret_cast<dnsheader*>(query), dq.size, dq.len, false, &queryRealTime);
+#ifdef HAVE_PROTOBUF
+ dr.uniqueId = dq.uniqueId;
+#endif
+ dr.qTag = dq.qTag;
+
+ if (!processResponse(holders.selfAnsweredRespRulactions, dr, &delayMsec)) {
+ goto drop;
+ }
+
#ifdef HAVE_DNSCRYPT
if (!encryptResponse(query, &dq.len, dq.size, true, dnsCryptQuery, nullptr, nullptr)) {
goto drop;
auto responseRules = someResponseRulesToJson(&g_resprulactions);
auto cacheHitResponseRules = someResponseRulesToJson(&g_cachehitresprulactions);
+ auto selfAnsweredResponseRules = someResponseRulesToJson(&g_selfansweredresprulactions);
string acl;
{ "rules", rules},
{ "response-rules", responseRules},
{ "cache-hit-response-rules", cacheHitResponseRules},
+ { "self-answered-response-rules", selfAnsweredResponseRules},
{ "acl", acl},
{ "local", localaddresses}
};
GlobalStateHolder<vector<DNSDistRuleAction> > g_rulactions;
GlobalStateHolder<vector<DNSDistResponseRuleAction> > g_resprulactions;
GlobalStateHolder<vector<DNSDistResponseRuleAction> > g_cachehitresprulactions;
+GlobalStateHolder<vector<DNSDistResponseRuleAction> > g_selfansweredresprulactions;
Rings g_rings;
QueryCount g_qcount;
char* response = query;
uint16_t responseLen = dq.len;
+ DNSResponse dr(dq.qname, dq.qtype, dq.qclass, dq.local, dq.remote, reinterpret_cast<dnsheader*>(response), dq.size, responseLen, false, &realTime);
+#ifdef HAVE_PROTOBUF
+ dr.uniqueId = dq.uniqueId;
+#endif
+ dr.qTag = dq.qTag;
+
+ if (!processResponse(holders.selfAnsweredRespRulactions, dr, &delayMsec)) {
+ return;
+ }
+
#ifdef HAVE_DNSCRYPT
if (!encryptResponse(response, &responseLen, dq.size, false, dnsCryptQuery, nullptr, nullptr)) {
return;
dq.dh->rcode = RCode::ServFail;
dq.dh->qr = true;
+ DNSResponse dr(dq.qname, dq.qtype, dq.qclass, dq.local, dq.remote, reinterpret_cast<dnsheader*>(response), dq.size, responseLen, false, &realTime);
+#ifdef HAVE_PROTOBUF
+ dr.uniqueId = dq.uniqueId;
+#endif
+ dr.qTag = dq.qTag;
+
+ if (!processResponse(holders.selfAnsweredRespRulactions, dr, &delayMsec)) {
+ return;
+ }
+
#ifdef HAVE_DNSCRYPT
if (!encryptResponse(response, &responseLen, dq.size, false, dnsCryptQuery, nullptr, nullptr)) {
return;
extern GlobalStateHolder<vector<DNSDistRuleAction> > g_rulactions;
extern GlobalStateHolder<vector<DNSDistResponseRuleAction> > g_resprulactions;
extern GlobalStateHolder<vector<DNSDistResponseRuleAction> > g_cachehitresprulactions;
+extern GlobalStateHolder<vector<DNSDistResponseRuleAction> > g_selfansweredresprulactions;
extern GlobalStateHolder<NetmaskGroup> g_ACL;
extern ComboAddress g_serverControl; // not changed during runtime
struct LocalHolders
{
- LocalHolders(): acl(g_ACL.getLocal()), policy(g_policy.getLocal()), rulactions(g_rulactions.getLocal()), cacheHitRespRulactions(g_cachehitresprulactions.getLocal()), servers(g_dstates.getLocal()), dynNMGBlock(g_dynblockNMG.getLocal()), dynSMTBlock(g_dynblockSMT.getLocal()), pools(g_pools.getLocal())
+ LocalHolders(): acl(g_ACL.getLocal()), policy(g_policy.getLocal()), rulactions(g_rulactions.getLocal()), cacheHitRespRulactions(g_cachehitresprulactions.getLocal()), selfAnsweredRespRulactions(g_selfansweredresprulactions.getLocal()), servers(g_dstates.getLocal()), dynNMGBlock(g_dynblockNMG.getLocal()), dynSMTBlock(g_dynblockSMT.getLocal()), pools(g_pools.getLocal())
{
}
LocalStateHolder<ServerPolicy> policy;
LocalStateHolder<vector<DNSDistRuleAction> > rulactions;
LocalStateHolder<vector<DNSDistResponseRuleAction> > cacheHitRespRulactions;
+ LocalStateHolder<vector<DNSDistResponseRuleAction> > selfAnsweredRespRulactions;
LocalStateHolder<servers_t> servers;
LocalStateHolder<NetmaskTree<DynBlock> > dynNMGBlock;
LocalStateHolder<SuffixMatchTree<DynBlock> > dynSMTBlock;
:>json string acl: A string of comma-separated netmasks currently allowed by the :ref:`ACL <ACL>`.
:>json list cache-hit-response-rules: A list of :json:object:`ResponseRule` objects applied on cache hits
+ :>json list self-answered-response-rules: A list of :json:object:`ResponseRule` objects applied on self-answered queries
:>json string daemon_type: The type of daemon, always "dnsdist"
:>json list frontends: A list of :json:object:`Frontend` objects
:>json list pools: A list of :json:object:`Pool` objects
Move the last response rule to the first position.
-Functions for manipulation Cache Hit Rules:
+Functions for manipulating Cache Hit Respone Rules:
-.. function:: addCacheHitResponseAction(DNSRule, action)
+.. function:: addCacheHitResponseAction(DNSRule, action [, options])
.. versionadded:: 1.2.0
Move the last cache hit response rule to the first position.
+Functions for manipulating Self-Answered Response Rules:
+
+.. function:: addSelfAnsweredResponseAction(DNSRule, action [, options])
+
+ .. versionadded:: 1.3.0
+
+ Add a Rule and Action for Self-Answered queries to the existing rules.
+
+ :param DNSRule: A DNSRule, e.g. an :func:`allRule` or a compounded bunch of rules using e.g. :func:`AndRule`
+ :param action: The action to take
+
+.. function:: mvSelfAnsweredResponseRule(from, to)
+
+ .. versionadded:: 1.3.0
+
+ Move self answered response rule ``from`` to a position where it is in front of ``to``.
+ ``to`` can be one larger than the largest rule, in which case the rule will be moved to the last position.
+
+ :param int from: Rule number to move
+ :param int to: Location to more the Rule to
+
+.. function:: rmSelfAnsweredResponseRule(id)
+
+ .. versionadded:: 1.3.0
+
+ Remove self answered response rule ``id``.
+
+ :param int id: The UUID of the rule to remove if ``id`` is an UUID, its position otherwise
+
+.. function:: showSelfAnsweredResponseRules([showUUIDs])
+
+ .. versionadded:: 1.3.0
+
+ Show all defined self answered response rules, optionally displaying their UUIDs.
+
+ :param bool showUUIDs: Whether to display the UUIDs, defaults to false
+
+.. function:: topSelfAnsweredResponseRule()
+
+ .. versionadded:: 1.3.0
+
+ Move the last self answered response rule to the first position.
+
.. _RulesIntro:
Matching Packets (Selectors)
:param {Rule} selector: A table of Rules
-Convience Functions
-~~~~~~~~~~~~~~~~~~~
+Convenience Functions
+~~~~~~~~~~~~~~~~~~~~~
.. function:: makeRule(rule)
self.assertEquals(content['daemon_type'], 'dnsdist')
- rule_groups = ['response-rules', 'cache-hit-response-rules']
+ rule_groups = ['response-rules', 'cache-hit-response-rules', 'self-answered-response-rules']
for key in ['version', 'acl', 'local', 'rules', 'servers', 'frontends', 'pools'] + rule_groups:
self.assertIn(key, content)
--- /dev/null
+#!/usr/bin/env python
+import dns
+from dnsdisttests import DNSDistTest
+
+class TestSelfAnsweredResponses(DNSDistTest):
+
+ _config_template = """
+ -- this is a silly test config, please do not do this in production.
+ addAction(makeRule("udp.selfanswered.tests.powerdns.com."), SpoofAction("192.0.2.1"))
+ addSelfAnsweredResponseAction(AndRule({makeRule("udp.selfanswered.tests.powerdns.com."), NotRule(MaxQPSRule(1))}), DropResponseAction())
+ addAction(makeRule("tcp.selfanswered.tests.powerdns.com."), SpoofAction("192.0.2.1"))
+ addSelfAnsweredResponseAction(AndRule({makeRule("tcp.selfanswered.tests.powerdns.com."), NotRule(MaxQPSRule(1))}), DropResponseAction())
+ newServer{address="127.0.0.1:%s"}
+ """
+
+ def testSelfAnsweredUDP(self):
+ """
+ CacheHitResponse: Drop when served from the cache
+ """
+ ttl = 60
+ name = 'udp.selfanswered.tests.powerdns.com.'
+ query = dns.message.make_query(name, 'A', 'IN')
+ response = dns.message.make_response(query)
+ rrset = dns.rrset.from_text(name,
+ ttl,
+ dns.rdataclass.IN,
+ dns.rdatatype.A,
+ '192.0.2.1')
+ response.answer.append(rrset)
+ response.flags |= dns.flags.RA
+
+ # self-answered, but no SelfAnswered rule matches.
+ (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
+ self.assertTrue(receivedResponse)
+ self.assertEquals(receivedResponse, response)
+
+ # self-answered, AND SelfAnswered rule matches. Should not see a reply.
+ (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
+ self.assertIsNone(receivedResponse)
+
+ def testSelfAnsweredTCP(self):
+ """
+ testSelfAnsweredTCP: Drop after exceeding QPS
+ """
+ ttl = 60
+ name = 'tcp.selfanswered.tests.powerdns.com.'
+ query = dns.message.make_query(name, 'A', 'IN')
+ response = dns.message.make_response(query)
+ rrset = dns.rrset.from_text(name,
+ ttl,
+ dns.rdataclass.IN,
+ dns.rdatatype.A,
+ '192.0.2.1')
+ response.answer.append(rrset)
+ response.flags |= dns.flags.RA
+
+ # self-answered, but no SelfAnswered rule matches.
+ (_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False)
+ self.assertTrue(receivedResponse)
+ self.assertEquals(receivedResponse, response)
+
+ # self-answered, AND SelfAnswered rule matches. Should not see a reply.
+ (_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False)
+ self.assertIsNone(receivedResponse)