]> granicus.if.org Git - pdns/commitdiff
dnsdist: Add an option to return ServFail when no server is available
authorRemi Gacogne <remi.gacogne@powerdns.com>
Fri, 2 Dec 2016 14:05:36 +0000 (15:05 +0100)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Mon, 5 Dec 2016 16:26:57 +0000 (17:26 +0100)
pdns/README-dnsdist.md
pdns/dnsdist-console.cc
pdns/dnsdist-lua2.cc
pdns/dnsdist-tcp.cc
pdns/dnsdist.cc
pdns/dnsdist.hh
regression-tests.dnsdist/test_Routing.py

index 1f8455e9fbf45acbf7aa379982e271e0a8adf130..73e1ec87e3aa25fa7768bd550131cc3e9a316d13 100644 (file)
@@ -1385,6 +1385,7 @@ instantiate a server with additional parameters
     * `setServerPolicyLua(name, function)`: set server selection policy to one named 'name' and provided by 'function'
     * `showServerPolicy()`: show name of currently operational server selection policy
     * `newServerPolicy(name, function)`: create a policy object from a Lua function
+    * `setServFailWhenNoServer(bool)`: if set, return a ServFail when no servers are available, instead of the default behaviour of dropping the query
  * Available policies:
     * `firstAvailable`: Pick first server that has not exceeded its QPS limit, ordered by the server 'order' parameter
     * `whashed`: Weighted hashed ('sticky') distribution over available servers, based on the server 'weight' parameter
index 437c4b552587ddf35c0c360866431854c4ef7e6f..65221eebc80b069ae355a9bfdda0091c02202559 100644 (file)
@@ -339,6 +339,7 @@ const std::vector<ConsoleKeyword> g_consoleKeywords{
   { "setRules", true, "list of rules", "replace the current rules with the supplied list of pairs of DNS Rules and DNS Actions (see `newRuleAction()`)" },
   { "setServerPolicy", true, "policy", "set server selection policy to that policy" },
   { "setServerPolicyLua", true, "name, function", "set server selection policy to one named 'name' and provided by 'function'" },
+  { "setServFailWhenNoServer", true, "bool", "if set, return a ServFail when no servers are available, instead of the default behaviour of dropping the query" },
   { "setTCPRecvTimeout", true, "n", "set the read timeout on TCP connections from the client, in seconds" },
   { "setTCPSendTimeout", true, "n", "set the write timeout on TCP connections from the client, in seconds" },
   { "setVerboseHealthChecks", true, "bool", "set whether health check errors will be logged" },
index 87330d33b175cb59f5687d2d67f016d3aa9dc1ef..8acc670da1ffff66676a8cd1fd86979ca5bc5137 100644 (file)
@@ -1117,4 +1117,9 @@ void moreLua(bool client)
           }
         }
       });
+
+    g_lua.writeFunction("setServFailWhenNoServer", [](bool servfail) {
+        setLuaSideEffect();
+        g_servFailOnNoPolicy = servfail;
+      });
 }
index 9d9f956872ece4168eb257e70316c0409025b670..9dc758c7dba59be0858238630b3a278e3ed76b1e 100644 (file)
@@ -333,10 +333,24 @@ void* tcpClientThread(int pipefd)
           g_stats.cacheMisses++;
         }
 
-       if(!ds) {
-         g_stats.noPolicy++;
-         break;
-       }
+        if(!ds) {
+          g_stats.noPolicy++;
+
+          if (g_servFailOnNoPolicy) {
+            restoreFlags(dh, origFlags);
+            dq.dh->rcode = RCode::ServFail;
+            dq.dh->qr = true;
+
+#ifdef HAVE_DNSCRYPT
+            if (!encryptResponse(queryBuffer, &dq.len, dq.size, true, dnsCryptQuery)) {
+              goto drop;
+            }
+#endif
+            sendResponseToClient(ci.fd, query, dq.len);
+          }
+
+          break;
+        }
 
        int dsock = -1;
        if(sockets.count(ds->remote) == 0) {
index 113967adb6288a0813e31d0d1775795f67aa101f..747bdcdfa16d998d3adb14300e252a1467b8b0a6 100644 (file)
@@ -134,6 +134,7 @@ DNSAction::Action g_dynBlockAction = DNSAction::Action::Drop;
 int g_tcpRecvTimeout{2};
 int g_tcpSendTimeout{2};
 
+bool g_servFailOnNoPolicy{false};
 bool g_truncateTC{1};
 bool g_fixupCase{0};
 static void truncateTC(const char* packet, uint16_t* len)
@@ -1112,7 +1113,23 @@ try
 
       if(!ss) {
        g_stats.noPolicy++;
-       continue;
+
+        if (g_servFailOnNoPolicy) {
+          char* response = query;
+          uint16_t responseLen = dq.len;
+          restoreFlags(dh, origFlags);
+
+          dq.dh->rcode = RCode::ServFail;
+          dq.dh->qr = true;
+
+#ifdef HAVE_DNSCRYPT
+          if (!encryptResponse(response, &responseLen, dq.size, false, dnsCryptQuery)) {
+            continue;
+          }
+#endif
+          sendUDPResponse(cs->udpFD, response, responseLen, 0, dest, remote);
+        }
+        continue;
       }
 
       ss->queries++;
index 4ada81837cd86151f1c7a2fe3225077b77d72079..306537cd0010eeec6bf5b6a5da597a15c1b248d4 100644 (file)
@@ -692,6 +692,7 @@ extern bool g_verboseHealthChecks;
 extern uint32_t g_staleCacheEntriesTTL;
 extern bool g_apiReadWrite;
 extern std::string g_apiConfigDirectory;
+extern bool g_servFailOnNoPolicy;
 
 struct ConsoleKeyword {
   std::string name;
index 807cdea4e6ba3acdb85b5eb0463ab18ef2eb8144..f26ba41162e1f07c2654a8570453e52daa5eab33 100644 (file)
@@ -357,7 +357,31 @@ class TestRoutingOrder(DNSDistTest):
             self.assertEquals(response, receivedResponse)
 
         total = 0
-        self.assertEquals(self._responsesCounter['UDP Responder'], 0)
+        if 'UDP Responder' in self._responsesCounter:
+            self.assertEquals(self._responsesCounter['UDP Responder'], 0)
         self.assertEquals(self._responsesCounter['UDP Responder 2'], numberOfQueries)
-        self.assertEquals(self._responsesCounter['TCP Responder'], 0)
+        if 'TCP Responder' in self._responsesCounter:
+            self.assertEquals(self._responsesCounter['TCP Responder'], 0)
         self.assertEquals(self._responsesCounter['TCP Responder 2'], numberOfQueries)
+
+class TestRoutingNoServer(DNSDistTest):
+
+    _config_template = """
+    newServer{address="127.0.0.1:%s", pool="real"}
+    setServFailWhenNoServer(true)
+    """
+
+    def testPolicyPoolNoServer(self):
+        """
+        Routing: No server should return ServFail
+        """
+        name = 'noserver.routing.tests.powerdns.com.'
+        query = dns.message.make_query(name, 'A', 'IN')
+        expectedResponse = dns.message.make_response(query)
+        expectedResponse.set_rcode(dns.rcode.SERVFAIL)
+
+        (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
+        self.assertEquals(receivedResponse, expectedResponse)
+
+        (_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False)
+        self.assertEquals(receivedResponse, expectedResponse)