]> git.ipfire.org Git - thirdparty/pdns.git/blobdiff - pdns/ws-auth.cc
Merge pull request #7787 from rgacogne/auth-api-replace-ent
[thirdparty/pdns.git] / pdns / ws-auth.cc
index 8d75ed1c74264834cb0309fb4687be5600400941..21396729caa9b996574d57932e3d98cd546a8dbf 100644 (file)
@@ -72,6 +72,7 @@ AuthWebServer::AuthWebServer() :
     d_ws = new WebServer(arg()["webserver-address"], arg().asNum("webserver-port"));
     d_ws->setApiKey(arg()["api-key"]);
     d_ws->setPassword(arg()["webserver-password"]);
+    d_ws->setLogLevel(arg()["webserver-loglevel"]);
 
     NetmaskGroup acl;
     acl.toMasks(::arg()["webserver-allow-from"]);
@@ -282,9 +283,10 @@ void AuthWebServer::indexfunction(HttpRequest* req, HttpResponse* resp)
 
   ret<<"Total queries: "<<S.read("udp-queries")<<". Question/answer latency: "<<S.read("latency")/1000.0<<"ms</p><br>"<<endl;
   if(req->getvars["ring"].empty()) {
-    vector<string>entries=S.listRings();
-    for(vector<string>::const_iterator i=entries.begin();i!=entries.end();++i)
-      printtable(ret,*i,S.getRingTitle(*i));
+    auto entries = S.listRings();
+    for(const auto &i: entries) {
+      printtable(ret, i, S.getRingTitle(i));
+    }
 
     printvars(ret);
     if(arg().mustDo("webserver-print-arguments"))
@@ -503,13 +505,24 @@ void productServerStatisticsFetch(map<string,string>& out)
   out["uptime"] = std::to_string(time(0) - s_starttime);
 }
 
+boost::optional<uint64_t> productServerStatisticsFetch(const std::string& name)
+{
+  try {
+    // ::read() calls ::exists() which throws a PDNSException when the key does not exist
+    return S.read(name);
+  }
+  catch(...) {
+    return boost::none;
+  }
+}
+
 static void validateGatheredRRType(const DNSResourceRecord& rr) {
   if (rr.qtype.getCode() == QType::OPT || rr.qtype.getCode() == QType::TSIG) {
     throw ApiException("RRset "+rr.qname.toString()+" IN "+rr.qtype.getName()+": invalid type given");
   }
 }
 
-static void gatherRecords(const Json container, const DNSName& qname, const QType qtype, const int ttl, vector<DNSResourceRecord>& new_records, vector<DNSResourceRecord>& new_ptrs) {
+static void gatherRecords(const string& logprefix, const Json container, const DNSName& qname, const QType qtype, const int ttl, vector<DNSResourceRecord>& new_records, vector<DNSResourceRecord>& new_ptrs) {
   UeberBackend B;
   DNSResourceRecord rr;
   rr.qname = qname;
@@ -545,6 +558,9 @@ static void gatherRecords(const Json container, const DNSName& qname, const QTyp
 
     if ((rr.qtype.getCode() == QType::A || rr.qtype.getCode() == QType::AAAA) &&
         boolFromJson(record, "set-ptr", false) == true) {
+
+      g_log<<Logger::Warning<<logprefix<<"API call uses deprecated set-ptr feature, please remove it"<<endl;
+
       DNSResourceRecord ptr;
       makePtr(rr, &ptr);
 
@@ -609,6 +625,11 @@ static void updateDomainSettingsFromDocument(UeberBackend& B, const DomainInfo&
     string master = value.string_value();
     if (master.empty())
       throw ApiException("Master can not be an empty string");
+    try {
+      ComboAddress m(master);
+    } catch (const PDNSException &e) {
+      throw ApiException("Master (" + master + ") is not an IP address: " + e.reason);
+    }
     zonemaster.push_back(master);
   }
 
@@ -1534,7 +1555,7 @@ static void apiServerZones(HttpRequest* req, HttpResponse* resp) {
         }
         if (rrset["records"].is_array()) {
           int ttl = intFromJson(rrset, "ttl");
-          gatherRecords(rrset, qname, qtype, ttl, new_records, new_ptrs);
+          gatherRecords(req->logprefix, rrset, qname, qtype, ttl, new_records, new_ptrs);
         }
         if (rrset["comments"].is_array()) {
           gatherComments(rrset, qname, qtype, new_comments);
@@ -1663,7 +1684,11 @@ static void apiServerZones(HttpRequest* req, HttpResponse* resp) {
       domains.push_back(di);
     }
   } else {
-    B.getAllDomains(&domains, true); // incl. disabled
+    try {
+      B.getAllDomains(&domains, true); // incl. disabled
+    } catch(const PDNSException &e) {
+      throw HttpInternalServerErrorException("Could not retrieve all domain information: " + e.reason);
+    }
   }
 
   Json::array doc;
@@ -1678,8 +1703,12 @@ static void apiServerZoneDetail(HttpRequest* req, HttpResponse* resp) {
 
   UeberBackend B;
   DomainInfo di;
-  if (!B.getDomainInfo(zonename, di)) {
-    throw HttpNotFoundException();
+  try {
+    if (!B.getDomainInfo(zonename, di)) {
+      throw HttpNotFoundException();
+    }
+  } catch(const PDNSException &e) {
+    throw HttpInternalServerErrorException("Could not retrieve Domain Info: " + e.reason);
   }
 
   if(req->method == "PUT") {
@@ -1696,6 +1725,11 @@ static void apiServerZoneDetail(HttpRequest* req, HttpResponse* resp) {
     if(!di.backend->deleteDomain(zonename))
       throw ApiException("Deleting domain '"+zonename.toString()+"' failed: backend delete failed/unsupported");
 
+    // clear caches
+    DNSSECKeeper dk(&B);
+    dk.clearCaches(zonename);
+    purgeAuthCaches(zonename.toString() + "$");
+
     // empty body on success
     resp->body = "";
     resp->status = 204; // No Content: declare that the zone is gone now
@@ -1952,7 +1986,7 @@ static void patchZone(HttpRequest* req, HttpResponse* resp) {
           // ttl shouldn't be part of DELETE, and it shouldn't be required if we don't get new records.
           int ttl = intFromJson(rrset, "ttl");
           // new_ptrs is merged.
-          gatherRecords(rrset, qname, qtype, ttl, new_records, new_ptrs);
+          gatherRecords(req->logprefix, rrset, qname, qtype, ttl, new_records, new_ptrs);
 
           for(DNSResourceRecord& rr : new_records) {
             rr.domain_id = di.id;
@@ -1976,8 +2010,10 @@ static void patchZone(HttpRequest* req, HttpResponse* resp) {
           di.backend->lookup(QType(QType::ANY), qname);
           DNSResourceRecord rr;
           while (di.backend->get(rr)) {
-            if (qtype.getCode() == 0) {
+            if (rr.qtype.getCode() == 0) {
               ent_present = true;
+              /* that's fine, we will override it */
+              continue;
             }
             if (qtype.getCode() != rr.qtype.getCode()
               && (exclusiveEntryTypes.count(qtype.getCode()) != 0
@@ -2058,9 +2094,20 @@ static void apiServerSearchData(HttpRequest* req, HttpResponse* resp) {
 
   string q = req->getvars["q"];
   string sMax = req->getvars["max"];
+  string sObjectType = req->getvars["object_type"];
+
   int maxEnts = 100;
   int ents = 0;
 
+  // the following types of data can be searched for using the api
+  enum class ObjectType
+  {
+    ALL,
+    ZONE,
+    RECORD,
+    COMMENT
+  } objectType;
+
   if (q.empty())
     throw ApiException("Query q can't be blank");
   if (!sMax.empty())
@@ -2068,6 +2115,19 @@ static void apiServerSearchData(HttpRequest* req, HttpResponse* resp) {
   if (maxEnts < 1)
     throw ApiException("Maximum entries must be larger than 0");
 
+  if (sObjectType.empty())
+    objectType = ObjectType::ALL;
+  else if (sObjectType == "all")
+    objectType = ObjectType::ALL;
+  else if (sObjectType == "zone")
+    objectType = ObjectType::ZONE;
+  else if (sObjectType == "record")
+    objectType = ObjectType::RECORD;
+  else if (sObjectType == "comment")
+    objectType = ObjectType::COMMENT;
+  else
+    throw ApiException("object_type must be one of the following options: all, zone, record, comment");
+
   SimpleMatch sm(q,true);
   UeberBackend B;
   vector<DomainInfo> domains;
@@ -2081,7 +2141,7 @@ static void apiServerSearchData(HttpRequest* req, HttpResponse* resp) {
 
   for(const DomainInfo di: domains)
   {
-    if (ents < maxEnts && sm.match(di.zone)) {
+    if ((objectType == ObjectType::ALL || objectType == ObjectType::ZONE) && ents < maxEnts && sm.match(di.zone)) {
       doc.push_back(Json::object {
         { "object_type", "zone" },
         { "zone_id", apiZoneNameToId(di.zone) },
@@ -2092,7 +2152,7 @@ static void apiServerSearchData(HttpRequest* req, HttpResponse* resp) {
     zoneIdZone[di.id] = di; // populate cache
   }
 
-  if (B.searchRecords(q, maxEnts, result_rr))
+  if ((objectType == ObjectType::ALL || objectType == ObjectType::RECORD) && B.searchRecords(q, maxEnts, result_rr))
   {
     for(const DNSResourceRecord& rr: result_rr)
     {
@@ -2115,7 +2175,7 @@ static void apiServerSearchData(HttpRequest* req, HttpResponse* resp) {
     }
   }
 
-  if (B.searchComments(q, maxEnts, result_c))
+  if ((objectType == ObjectType::ALL || objectType == ObjectType::COMMENT) && B.searchComments(q, maxEnts, result_c))
   {
     for(const Comment &c: result_c)
     {