From b1902fabbdc5410385390ba60c8698bca9e5b158 Mon Sep 17 00:00:00 2001 From: Christian Hofstaedtler Date: Mon, 10 Mar 2014 13:28:44 +0100 Subject: [PATCH] JSON API: add search-data Searches for zones, records, comments. --- pdns/ws-auth.cc | 71 ++++++++++++++++++++++++++++++ regression-tests.api/test_Zones.py | 17 +++++++ 2 files changed, 88 insertions(+) diff --git a/pdns/ws-auth.cc b/pdns/ws-auth.cc index 4eaf1012ff..f0bc3d5dd0 100644 --- a/pdns/ws-auth.cc +++ b/pdns/ws-auth.cc @@ -725,6 +725,76 @@ static void apiServerZoneRRset(HttpRequest* req, HttpResponse* resp) { resp->body = "{}"; } +static void apiServerSearchData(HttpRequest* req, HttpResponse* resp) { + if(req->method != "GET") + throw HttpMethodNotAllowedException(); + + string q = req->parameters["q"]; + if (q.empty()) + throw ApiException("Query q can't be blank"); + + UeberBackend B; + + vector domains; + B.getAllDomains(&domains, true); // incl. disabled + + Document doc; + doc.SetArray(); + + DNSResourceRecord rr; + Comment comment; + + BOOST_FOREACH(const DomainInfo& di, domains) { + if (di.zone.find(q) != string::npos) { + Value object; + object.SetObject(); + object.AddMember("type", "zone", doc.GetAllocator()); + Value jname(di.zone.c_str(), doc.GetAllocator()); // copy + object.AddMember("name", jname, doc.GetAllocator()); + doc.PushBack(object, doc.GetAllocator()); + } + + // if zone name is an exact match, don't bother with returning all records/comments in it + if (di.zone == q) { + continue; + } + + di.backend->list(di.zone, di.id, true); // incl. disabled + while(di.backend->get(rr)) { + if (!rr.qtype.getCode()) + continue; // skip empty non-terminals + + if (rr.qname.find(q) == string::npos && rr.content.find(q) == string::npos) + continue; + + Value object; + object.SetObject(); + object.AddMember("type", "record", doc.GetAllocator()); + Value jname(rr.qname.c_str(), doc.GetAllocator()); // copy + object.AddMember("name", jname, doc.GetAllocator()); + Value jcontent(rr.content.c_str(), doc.GetAllocator()); // copy + object.AddMember("content", jcontent, doc.GetAllocator()); + doc.PushBack(object, doc.GetAllocator()); + } + + di.backend->listComments(di.id); + while(di.backend->getComment(comment)) { + if (comment.qname.find(q) == string::npos && comment.content.find(q) == string::npos) + continue; + + Value object; + object.SetObject(); + object.AddMember("type", "comment", doc.GetAllocator()); + Value jname(comment.qname.c_str(), doc.GetAllocator()); // copy + object.AddMember("name", jname, doc.GetAllocator()); + Value jcontent(comment.content.c_str(), doc.GetAllocator()); // copy + object.AddMember("content", jcontent, doc.GetAllocator()); + doc.PushBack(object, doc.GetAllocator()); + } + } + resp->setBody(doc); +} + void AuthWebServer::jsonstat(HttpRequest* req, HttpResponse* resp) { string command; @@ -825,6 +895,7 @@ void AuthWebServer::webThread() if(::arg().mustDo("experimental-json-interface")) { d_ws->registerApiHandler("/servers/localhost/config", &apiServerConfig); d_ws->registerApiHandler("/servers/localhost/search-log", &apiServerSearchLog); + d_ws->registerApiHandler("/servers/localhost/search-data", &apiServerSearchData); d_ws->registerApiHandler("/servers/localhost/statistics", &apiServerStatistics); d_ws->registerApiHandler("/servers/localhost/zones//rrset", &apiServerZoneRRset); d_ws->registerApiHandler("/servers/localhost/zones/", &apiServerZoneDetail); diff --git a/regression-tests.api/test_Zones.py b/regression-tests.api/test_Zones.py index 3ccee84ac5..14a0b06dc8 100644 --- a/regression-tests.api/test_Zones.py +++ b/regression-tests.api/test_Zones.py @@ -492,6 +492,23 @@ class AuthZones(ApiTestCase): u'name': u'a.a.0.0.b.b.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa' }]) + def test_SearchRRExactZone(self): + name = unique_zone_name() + self.create_zone(name=name) + r = self.session.get(self.url("/servers/localhost/search-data?q=" + name)) + self.assertSuccessJson(r) + print r.json() + self.assertEquals(r.json(), [{u'type': u'zone', u'name': name}]) + + def test_SearchRRSubstring(self): + name = 'search-rr-zone.name' + self.create_zone(name=name) + r = self.session.get(self.url("/servers/localhost/search-data?q=rr-zone")) + self.assertSuccessJson(r) + print r.json() + # should return zone, SOA, ns1, ns2 + self.assertEquals(len(r.json()), 4) + @unittest.skipIf(not isRecursor(), "Not applicable") class RecursorZones(ApiTestCase): -- 2.47.2