]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
Recursor: Add allow-from(-file) REST API
authorChristian Hofstaedtler <christian@hofstaedtler.name>
Thu, 6 Feb 2014 17:04:15 +0000 (18:04 +0100)
committerChristian Hofstaedtler <christian@hofstaedtler.name>
Fri, 7 Feb 2014 14:11:02 +0000 (15:11 +0100)
pdns/iputils.hh
pdns/pdns_recursor.cc
pdns/syncres.hh
pdns/ws-recursor.cc
regression-tests.api/.gitignore
regression-tests.api/runtests.py
regression-tests.api/test_RecursorConfig.py [new file with mode: 0644]

index 3a9ef70376206382d937d25bac31d32cbb886ecc..decbb606dc70a469b1f9212d5bb5e00a435b579c 100644 (file)
@@ -364,6 +364,13 @@ public:
     return str.str();
   }
 
+  void toStringVector(vector<string>* vec) const
+  {
+    for(container_t::const_iterator iter = d_masks.begin(); iter != d_masks.end(); ++iter) {
+      vec->push_back(iter->toString());
+    }
+  }
+
   void toMasks(const string &ips)
   {
     vector<string> parts;
index c30a5eada59187c83b9a263a2025dc84ed01bfa9..88b9b61a79e653e3aaf11269896a300fc35f14b6 100644 (file)
@@ -106,7 +106,7 @@ bool g_quiet;
 
 bool g_weDistributeQueries; // if true, only 1 thread listens on the incoming query sockets
 
-static __thread NetmaskGroup* t_allowFrom;
+__thread NetmaskGroup* t_allowFrom;
 static NetmaskGroup* g_initialAllowFrom; // new thread needs to be setup with this
 
 NetmaskGroup* g_dontQuery;
index b6b4920d9d96fa753d01de262ade7a5f265f8595..0b6d7c1e11ee4d6c4aedcecfd14136ae81c45237 100644 (file)
@@ -548,6 +548,7 @@ struct RemoteKeeper
   }
 };
 extern __thread RemoteKeeper* t_remotes;
+extern __thread NetmaskGroup* t_allowFrom;
 string doQueueReloadLuaScript(vector<string>::const_iterator begin, vector<string>::const_iterator end);
 string doTraceRegex(vector<string>::const_iterator begin, vector<string>::const_iterator end);
 void parseACLs();
index e5b857dc2fbf1c6a3be06def1b77ac09570d7462..4bef015a998328d898afd0587ee251ef96116679 100644 (file)
@@ -35,6 +35,7 @@
 #include "rapidjson/writer.h"
 #include "webserver.hh"
 #include "ws-api.hh"
+#include "logger.hh"
 
 using namespace rapidjson;
 
@@ -60,6 +61,51 @@ static void apiWriteConfigFile(const string& filebasename, const string& content
   ofconf.close();
 }
 
+static void apiServerConfigAllowFrom(HttpRequest* req, HttpResponse* resp)
+{
+  if (req->method == "PUT") {
+    Document document;
+    req->json(document);
+
+    if (!document.IsArray()) {
+      throw ApiException("Supplied JSON must be an array");
+    }
+
+    ostringstream ss;
+
+    // Clear allow-from-file if set, so our changes take effect
+    ss << "allow-from-file=" << endl;
+
+    // Clear allow-from, and provide a "parent" value
+    ss << "allow-from=" << endl;
+    for (SizeType i = 0; i < document.Size(); ++i) {
+      ss << "allow-from+=" << document[i].GetString() << endl;
+    }
+
+    apiWriteConfigFile("allow-from", ss.str());
+
+    parseACLs();
+
+    // fall through to GET
+  } else if (req->method != "GET") {
+    throw HttpMethodNotAllowedException();
+  }
+
+  // Return currently configured ACLs
+  Document document;
+  document.SetArray();
+
+  vector<string> entries;
+  t_allowFrom->toStringVector(&entries);
+
+  BOOST_FOREACH(const string& entry, entries) {
+    Value jentry(entry.c_str(), document.GetAllocator()); // copy
+    document.PushBack(jentry, document.GetAllocator());
+  }
+
+  resp->setBody(document);
+}
+
 RecursorWebServer::RecursorWebServer(FDMultiplexer* fdm)
 {
   RecursorControlParser rcp; // inits
@@ -73,6 +119,7 @@ RecursorWebServer::RecursorWebServer(FDMultiplexer* fdm)
 
   // legacy dispatch
   d_ws->registerApiHandler("/jsonstat", boost::bind(&RecursorWebServer::jsonstat, this, _1, _2));
+  d_ws->registerApiHandler("/servers/localhost/config/allow-from", &apiServerConfigAllowFrom);
   d_ws->registerApiHandler("/servers/localhost/config", &apiServerConfig);
   d_ws->registerApiHandler("/servers/localhost/search-log", &apiServerSearchLog);
   d_ws->registerApiHandler("/servers/localhost/statistics", &apiServerStatistics);
index b5d3e23b1a3e37cea9a1663f8ee5d9c64d15b18c..1fd0d7702399cde3eb87450efe930c26dda4e532 100644 (file)
@@ -5,3 +5,4 @@
 /*.controlsocket
 /named.conf
 /*.pyc
+acl.conf
index c6caeff6e3a0282fca74719b73b73fb1708254ee..742b75d5745787130bfe65b7e22503f1f928f434 100755 (executable)
@@ -17,6 +17,13 @@ options { directory "../regression-tests/zones/"; };
 zone "example.com" { type master; file "example.com"; };
 """
 
+ACL_LIST_TPL = """
+# Generated by runtests.py
+# local host
+127.0.0.1
+::1
+"""
+
 daemon = (len(sys.argv) == 2) and sys.argv[1] or None
 if daemon not in ('authoritative', 'recursor'):
     print "Usage: ./runtests (authoritative|recursor)"
@@ -48,9 +55,10 @@ if daemon == 'authoritative':
     pdnscmd = ("../pdns/pdns_server --daemon=no --local-port=5300 --socket-dir=./ --no-shuffle --launch=gsqlite3 --gsqlite3-dnssec --send-root-referral --allow-2136-from=127.0.0.0/8 --experimental-rfc2136=yes --cache-ttl=0 --no-config --gsqlite3-database="+SQLITE_DB+" --experimental-json-interface=yes --webserver=yes --webserver-port="+WEBPORT+" --webserver-address=127.0.0.1 --query-logging  --webserver-password="+WEBPASSWORD).split()
 
 else:
+    with open('acl.list', 'w') as acl_list:
+        acl_list.write(ACL_LIST_TPL)
 
-    # No preparations for recursor
-    pdnscmd = ("../pdns/pdns_recursor --daemon=no --socket-dir=. --local-port=5555 --experimental-json-interface=yes --experimental-webserver=yes --experimental-webserver-port="+WEBPORT+" --experimental-webserver-address=127.0.0.1 --experimental-webserver-password="+WEBPASSWORD).split()
+    pdnscmd = ("../pdns/pdns_recursor --daemon=no --socket-dir=. --config-dir=. --allow-from-file=acl.list --local-port=5555 --experimental-json-interface=yes --experimental-webserver=yes --experimental-webserver-port="+WEBPORT+" --experimental-webserver-address=127.0.0.1 --experimental-webserver-password="+WEBPASSWORD).split()
 
 
 # Now run pdns and the tests.
diff --git a/regression-tests.api/test_RecursorConfig.py b/regression-tests.api/test_RecursorConfig.py
new file mode 100644 (file)
index 0000000..7e10b66
--- /dev/null
@@ -0,0 +1,22 @@
+import json
+import requests
+import unittest
+from test_helper import ApiTestCase, isRecursor
+
+
+@unittest.skipIf(not isRecursor(), "Only applicable to recursors")
+class RecursorConfig(ApiTestCase):
+
+    def test_ConfigAllowFromGet(self):
+        r = self.session.get(self.url("/servers/localhost/config/allow-from"))
+        self.assertSuccessJson(r)
+
+    def test_ConfigAllowFromReplace(self):
+        payload = ["127.0.0.1"]
+        r = self.session.put(
+            self.url("/servers/localhost/config/allow-from"),
+            data=json.dumps(payload),
+            headers={'content-type': 'application/json'})
+        self.assertSuccessJson(r)
+        data = r.json()
+        self.assertEquals("127.0.0.1/32", data[0])