]> granicus.if.org Git - pdns/commitdiff
dnsdist: Add `includeDirectory(dir)`
authorRemi Gacogne <remi.gacogne@powerdns.com>
Thu, 3 Nov 2016 11:40:25 +0000 (12:40 +0100)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Thu, 3 Nov 2016 11:40:25 +0000 (12:40 +0100)
pdns/README-dnsdist.md
pdns/dnsdist-lua2.cc
regression-tests.dnsdist/test-include-dir/test.conf [new file with mode: 0644]
regression-tests.dnsdist/test_Advanced.py

index 43ae7bbd6764bc6cd4884df62b3305974a9bce7e..960c90531204f66a0e63f8f52f944a8ef4da686a 100644 (file)
@@ -1223,6 +1223,7 @@ Here are all functions:
     * `shutdown()`: shut down `dnsdist`
     * quit or ^D: exit the console
     * `webserver(address:port, password [, apiKey [, customHeaders ]])`: launch a webserver with stats on that address with that password
+    * `includeDirectory(dir)`: all files ending in `.conf` in the directory `dir` are loaded into the configuration
  * ACL related:
     * `addACL(netmask)`: add to the ACL set who can use this server
     * `setACL({netmask, netmask})`: replace the ACL set with these netmasks. Use `setACL({})` to reset the list, meaning no one can use us
index f578774e80891b1b6eb5a0fe732f2543df799faa..099fbf50331f7b788b6f90176d1503d994838c88 100644 (file)
 #include <fstream>
 #include <boost/logic/tribool.hpp>
 #include "statnode.hh"
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <unistd.h>
 
 boost::tribool g_noLuaSideEffect;
+static bool g_included{false};
 
 /* this is a best effort way to prevent logging calls with no side-effects in the output of delta()
    Functions can declare setLuaNoSideEffect() and if nothing else does declare a side effect, or nothing
@@ -1034,4 +1039,68 @@ void moreLua(bool client)
         }
         return res;
       });
+
+    g_lua.writeFunction("includeDirectory", [](const std::string& dirname) {
+        if (g_configurationDone) {
+          errlog("includeDirectory() cannot be used at runtime!");
+          g_outputBuffer="includeDirectory() cannot be used at runtime!\n";
+          return;
+        }
+
+        if (g_included) {
+          errlog("includeDirectory() cannot be used recursively!");
+          g_outputBuffer="includeDirectory() cannot be used recursively!\n";
+          return;
+        }
+
+        g_included = true;
+        struct stat st;
+
+        if (stat(dirname.c_str(), &st)) {
+          errlog("The included directory %s does not exist!", dirname.c_str());
+          g_outputBuffer="The included directory " + dirname + " does not exist!";
+          return;
+        }
+
+        if (!S_ISDIR(st.st_mode)) {
+          errlog("The included directory %s is not a directory!", dirname.c_str());
+          g_outputBuffer="The included directory " + dirname + " is not a directory!";
+          return;
+        }
+
+        DIR *dirp;
+        struct dirent *ent;
+        if (!(dirp = opendir(dirname.c_str()))) {
+          errlog("Error opening the included directory %s!", dirname.c_str());
+          g_outputBuffer="Error opening the included directory " + dirname + "!";
+          return;
+        }
+
+        while((ent = readdir(dirp)) != NULL) {
+          if (ent->d_name[0] == '.') {
+            continue;
+          }
+
+          if (boost::ends_with(ent->d_name, ".conf")) {
+            std::ostringstream namebuf;
+            namebuf << dirname.c_str() << "/" << ent->d_name;
+
+            if (stat(namebuf.str().c_str(), &st) || !S_ISREG(st.st_mode)) {
+              continue;
+            }
+
+            std::ifstream ifs(namebuf.str());
+            if (!ifs) {
+              warnlog("Unable to read configuration from '%s'", namebuf.str());
+            } else {
+              vinfolog("Read configuration from '%s'", namebuf.str());
+            }
+
+            g_lua.executeCode(ifs);
+          }
+        }
+        closedir(dirp);
+
+        g_included = false;
+    });
 }
diff --git a/regression-tests.dnsdist/test-include-dir/test.conf b/regression-tests.dnsdist/test-include-dir/test.conf
new file mode 100644 (file)
index 0000000..b0926f6
--- /dev/null
@@ -0,0 +1 @@
+addAction(makeRule("includedir.advanced.tests.powerdns.com."), AllowAction())
index d98392fd73e202bd6246bc6f45a5a44bd11cd8b5..8f4b77906c10fbd0c573d78e789bb7b5c4d42cc3 100644 (file)
@@ -1136,3 +1136,52 @@ class TestAdvancedWireLengthRule(DNSDistTest):
 
         (_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False)
         self.assertEquals(receivedResponse, expectedResponse)
+
+class TestAdvancedIncludeDir(DNSDistTest):
+
+    _config_template = """
+    -- this directory contains a file allowing includedir.advanced.tests.powerdns.com.
+    includeDirectory('test-include-dir')
+    addAction(AllRule(), RCodeAction(dnsdist.REFUSED))
+    newServer{address="127.0.0.1:%s"}
+    """
+
+    def testAdvancedIncludeDirAllowed(self):
+        """
+        Advanced: includeDirectory()
+        """
+        name = 'includedir.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)
+
+        # this one should be refused
+        name = 'notincludedir.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)