* `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
#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
}
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;
+ });
}
(_, 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)