with `clearDynBlocks()`. Full set of `exceed` functions is listed in the table of
all functions below.
+Dynamic blocks drop matched queries by default, but this behavior can be changed
+with `setDynBlocksAction()`. For example, to send a REFUSED code instead of droppping
+the query:
+
+```
+setDynBlocksAction(DNSAction.Refused)
+```
Running it for real
-------------------
* `clearDynBlocks()`: clear all dynamic blocks
* `showDynBlocks()`: show dynamic blocks in force
* `addDynBlocks(addresses, message[, seconds])`: block the set of addresses with message `msg`, for `seconds` seconds (10 by default)
+ * `setDynBlocksAction(DNSAction)`: set which action is performed when a query is blocked. Only DNSAction.Drop (the default) and DNSAction.Refused are supported
* `addBPFFilterDynBlocks(addresses, DynBPFFilter[, seconds])`: block the set of addresses using the supplied BPF Filter, for `seconds` seconds (10 by default)
* `exceedServFails(rate, seconds)`: get set of addresses that exceed `rate` servails/s over `seconds` seconds
* `exceedNXDOMAINs(rate, seconds)`: get set of addresses that exceed `rate` NXDOMAIN/s over `seconds` seconds
typedef std::unordered_map<std::string, boost::variant<bool, std::string, vector<pair<int, std::string> > > > newserver_t;
g_lua.writeVariable("DNSAction", std::unordered_map<string,int>{
- {"Drop", (int)DNSAction::Action::Drop},
- {"Nxdomain", (int)DNSAction::Action::Nxdomain},
- {"Spoof", (int)DNSAction::Action::Spoof},
- {"Allow", (int)DNSAction::Action::Allow},
+ {"Drop", (int)DNSAction::Action::Drop},
+ {"Nxdomain", (int)DNSAction::Action::Nxdomain},
+ {"Refused", (int)DNSAction::Action::Refused},
+ {"Spoof", (int)DNSAction::Action::Spoof},
+ {"Allow", (int)DNSAction::Action::Allow},
{"HeaderModify", (int)DNSAction::Action::HeaderModify},
- {"Pool", (int)DNSAction::Action::Pool},
+ {"Pool", (int)DNSAction::Action::Pool},
{"None",(int)DNSAction::Action::None},
{"Delay", (int)DNSAction::Action::Delay}}
);
g_dynblockSMT.setState(slow);
});
-
+ g_lua.writeFunction("setDynBlocksAction", [](DNSAction::Action action) {
+ if (!g_configurationDone) {
+ if (action == DNSAction::Action::Drop || action == DNSAction::Action::Refused) {
+ g_dynBlockAction = action;
+ }
+ else {
+ errlog("Dynamic blocks action can only be Drop or Refused!");
+ g_outputBuffer="Dynamic blocks action can only be Drop or Refused!\n";
+ }
+ } else {
+ g_outputBuffer="Dynamic blocks action cannot be altered at runtime!\n";
+ }
+ });
g_lua.registerFunction<bool(nmts_t::*)(const ComboAddress&)>("match",
[](nmts_t& s, const ComboAddress& ca) { return s.match(ca); });
GlobalStateHolder<servers_t> g_dstates;
GlobalStateHolder<NetmaskTree<DynBlock>> g_dynblockNMG;
GlobalStateHolder<SuffixMatchTree<DynBlock>> g_dynblockSMT;
+DNSAction::Action g_dynBlockAction = DNSAction::Action::Drop;
int g_tcpRecvTimeout{2};
int g_tcpSendTimeout{2};
if(auto got=localDynNMGBlock->lookup(*dq.remote)) {
if(now < got->second.until) {
- vinfolog("Query from %s dropped because of dynamic block", dq.remote->toStringWithPort());
g_stats.dynBlocked++;
got->second.blocks++;
- return false;
+ if (g_dynBlockAction == DNSAction::Action::Refused) {
+ vinfolog("Query from %s refused because of dynamic block", dq.remote->toStringWithPort());
+ dq.dh->rcode = RCode::Refused;
+ dq.dh->qr=true;
+ return true;
+ }
+ else {
+ vinfolog("Query from %s dropped because of dynamic block", dq.remote->toStringWithPort());
+ return false;
+ }
}
}
if(auto got=localDynSMTBlock->lookup(*dq.qname)) {
if(now < got->until) {
- vinfolog("Query from %s for %s dropped because of dynamic block", dq.remote->toStringWithPort(), dq.qname->toString());
g_stats.dynBlocked++;
got->blocks++;
- return false;
+ if (g_dynBlockAction == DNSAction::Action::Refused) {
+ vinfolog("Query from %s for %s refused because of dynamic block", dq.remote->toStringWithPort(), dq.qname->toString());
+ dq.dh->rcode = RCode::Refused;
+ dq.dh->qr=true;
+ return true;
+ }
+ else {
+ vinfolog("Query from %s for %s dropped because of dynamic block", dq.remote->toStringWithPort(), dq.qname->toString());
+ return false;
+ }
}
}
-
if(blockFilter) {
std::lock_guard<std::mutex> lock(g_luamutex);
g_stats.ruleNXDomain++;
return true;
break;
+ case DNSAction::Action::Refused:
+ dq.dh->rcode = RCode::Refused;
+ dq.dh->qr=true;
+ g_stats.ruleRefused++;
+ return true;
+ break;
case DNSAction::Action::Spoof:
spoofResponseFromString(dq, ruleresult);
return true;
stat_t dynBlocked{0};
stat_t ruleDrop{0};
stat_t ruleNXDomain{0};
+ stat_t ruleRefused{0};
stat_t selfAnswered{0};
stat_t downstreamTimeouts{0};
stat_t downstreamSendErrors{0};
typedef std::function<uint64_t(const std::string&)> statfunction_t;
typedef boost::variant<stat_t*, double*, statfunction_t> entry_t;
std::vector<std::pair<std::string, entry_t>> entries{
- {"responses", &responses}, {"servfail-responses", &servfailResponses},
- {"queries", &queries}, {"acl-drops", &aclDrops},
- {"block-filter", &blockFilter}, {"rule-drop", &ruleDrop},
- {"rule-nxdomain", &ruleNXDomain}, {"self-answered", &selfAnswered},
- {"downstream-timeouts", &downstreamTimeouts}, {"downstream-send-errors", &downstreamSendErrors},
- {"trunc-failures", &truncFail}, {"no-policy", &noPolicy},
- {"latency0-1", &latency0_1}, {"latency1-10", &latency1_10},
- {"latency10-50", &latency10_50}, {"latency50-100", &latency50_100},
- {"latency100-1000", &latency100_1000}, {"latency-slow", &latencySlow},
- {"latency-avg100", &latencyAvg100}, {"latency-avg1000", &latencyAvg1000},
- {"latency-avg10000", &latencyAvg10000}, {"latency-avg1000000", &latencyAvg1000000},
+ {"responses", &responses},
+ {"servfail-responses", &servfailResponses},
+ {"queries", &queries},
+ {"acl-drops", &aclDrops},
+ {"block-filter", &blockFilter},
+ {"rule-drop", &ruleDrop},
+ {"rule-nxdomain", &ruleNXDomain},
+ {"rule-refused", &ruleRefused},
+ {"self-answered", &selfAnswered},
+ {"downstream-timeouts", &downstreamTimeouts},
+ {"downstream-send-errors", &downstreamSendErrors},
+ {"trunc-failures", &truncFail},
+ {"no-policy", &noPolicy},
+ {"latency0-1", &latency0_1},
+ {"latency1-10", &latency1_10},
+ {"latency10-50", &latency10_50},
+ {"latency50-100", &latency50_100},
+ {"latency100-1000", &latency100_1000},
+ {"latency-slow", &latencySlow},
+ {"latency-avg100", &latencyAvg100},
+ {"latency-avg1000", &latencyAvg1000},
+ {"latency-avg10000", &latencyAvg10000},
+ {"latency-avg1000000", &latencyAvg1000000},
{"uptime", uptimeOfProcess},
{"real-memory-usage", getRealMemoryUsage},
{"noncompliant-queries", &nonCompliantQueries},
{"cache-misses", &cacheMisses},
{"cpu-user-msec", getCPUTimeUser},
{"cpu-sys-msec", getCPUTimeSystem},
- {"fd-usage", getOpenFileDescriptors}, {"dyn-blocked", &dynBlocked},
+ {"fd-usage", getOpenFileDescriptors},
+ {"dyn-blocked", &dynBlocked},
{"dyn-block-nmg-size", [](const std::string&) { return g_dynblockNMG.getLocal()->size(); }}
};
};
class DNSAction
{
public:
- enum class Action { Drop, Nxdomain, Spoof, Allow, HeaderModify, Pool, Delay, None};
+ enum class Action { Drop, Nxdomain, Refused, Spoof, Allow, HeaderModify, Pool, Delay, None};
virtual Action operator()(DNSQuestion*, string* ruleresult) const =0;
virtual string toString() const = 0;
virtual std::unordered_map<string, double> getStats() const
};
extern GlobalStateHolder<SuffixMatchTree<DynBlock>> g_dynblockSMT;
+extern DNSAction::Action g_dynBlockAction;
extern GlobalStateHolder<vector<CarbonConfig> > g_carbon;
extern GlobalStateHolder<ServerPolicy> g_policy;
values[entry['name']] = entry['value']
expected = ['responses', 'servfail-responses', 'queries', 'acl-drops', 'block-filter',
- 'rule-drop', 'rule-nxdomain', 'self-answered', 'downstream-timeouts',
+ 'rule-drop', 'rule-nxdomain', 'rule-refused', 'self-answered', 'downstream-timeouts',
'downstream-send-errors', 'trunc-failures', 'no-policy', 'latency0-1',
'latency1-10', 'latency10-50', 'latency50-100', 'latency100-1000',
'latency-slow', 'latency-avg100', 'latency-avg1000', 'latency-avg10000',
self.assertIn(key, values)
self.assertTrue(values[key] >= 0)
+ for key in values:
+ self.assertIn(key, expected)
+
def testJsonstatStats(self):
"""
API: /jsonstat?command=stats
self.assertTrue(r.json())
content = r.json()
- for key in ['packetcache-hits', 'packetcache-misses', 'over-capacity-drops', 'too-old-drops']:
- self.assertIn(key, content)
- self.assertTrue(content[key] >= 0)
-
expected = ['responses', 'servfail-responses', 'queries', 'acl-drops', 'block-filter',
- 'rule-drop', 'rule-nxdomain', 'self-answered', 'downstream-timeouts',
+ 'rule-drop', 'rule-nxdomain', 'rule-refused', 'self-answered', 'downstream-timeouts',
'downstream-send-errors', 'trunc-failures', 'no-policy', 'latency0-1',
'latency1-10', 'latency10-50', 'latency50-100', 'latency100-1000',
'latency-slow', 'latency-avg100', 'latency-avg1000', 'latency-avg10000',
'latency-avg1000000', 'uptime', 'real-memory-usage', 'noncompliant-queries',
'noncompliant-responses', 'rdqueries', 'empty-queries', 'cache-hits',
'cache-misses', 'cpu-user-msec', 'cpu-sys-msec', 'fd-usage', 'dyn-blocked',
- 'dyn-block-nmg-size']
+ 'dyn-block-nmg-size', 'packetcache-hits', 'packetcache-misses', 'over-capacity-drops',
+ 'too-old-drops']
for key in expected:
self.assertIn(key, content)