]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
When stumbling upon ill-formed records, return HTTP 422 rather than 500.
authorMiod Vallat <miod.vallat@powerdns.com>
Wed, 27 Aug 2025 08:46:24 +0000 (10:46 +0200)
committerMiod Vallat <miod.vallat@powerdns.com>
Wed, 10 Sep 2025 09:59:06 +0000 (11:59 +0200)
This allows the user to get a hopefully helpful error message to help
figure out the cause of the problem.

Fixes: #6673
Fixes: #7203
Signed-off-by: Miod Vallat <miod.vallat@powerdns.com>
pdns/ws-auth.cc

index 9585e0c17d0e31f6470b7c87e8edbf913321abd1..03e7af9a348dd158dedf4cb172ed46f3530951e3 100644 (file)
@@ -525,9 +525,21 @@ static void fillZone(UeberBackend& backend, const ZoneName& zonename, HttpRespon
 
       while (rit != records.end() && rit->qname == current_qname && rit->qtype == current_qtype) {
         ttl = min(ttl, rit->ttl);
+        std::string content;
+        try {
+          content = makeApiRecordContent(rit->qtype, rit->content);
+        }
+        catch (std::exception& e) {
+          // makeApiRecordContent may throw an exception if the backend data
+          // is not well-formed (e.g. corrupted bind zone file).
+          // The exception gets caught here and rethrown as ApiException in
+          // order to return a 422 error code with a (hopefully) useful error
+          // message instead of a 500 error.
+          throw ApiException("Ill-formed record contents found for " + current_qname.toString() + ": " + e.what());
+        }
         auto object = Json::object{
           {"disabled", rit->disabled},
-          {"content", makeApiRecordContent(rit->qtype, rit->content)}};
+          {"content", content}};
         if (rit->last_modified != 0) {
           object["modified_at"] = (double)rit->last_modified;
         }
@@ -2334,9 +2346,21 @@ static void apiServerZoneExport(HttpRequest* req, HttpResponse* resp)
       continue; // skip empty non-terminals
     }
 
+    std::string content;
+    try {
+      content = makeApiRecordContent(resourceRecord.qtype, resourceRecord.content);
+    }
+    catch (std::exception& e) {
+      // makeApiRecordContent may throw an exception if the backend data
+      // is not well-formed (e.g. corrupted bind zone file).
+      // The exception gets caught here and rethrown as ApiException in
+      // order to return a 422 error code with a (hopefully) useful error
+      // message instead of a 500 error.
+      throw ApiException("Ill-formed record contents found for " + resourceRecord.qname.toString() + ": " + e.what());
+    }
     outputStringStream << resourceRecord.qname.toString() << "\t" << resourceRecord.ttl << "\t"
                        << "IN"
-                       << "\t" << resourceRecord.qtype.toString() << "\t" << makeApiRecordContent(resourceRecord.qtype, resourceRecord.content) << endl;
+                       << "\t" << resourceRecord.qtype.toString() << "\t" << content << endl;
   }
 
   if (req->accept_json) {
@@ -2650,13 +2674,25 @@ static void apiServerSearchData(HttpRequest* req, HttpResponse* resp)
         continue; // skip empty non-terminals
       }
 
+      std::string content;
+      try {
+        content = makeApiRecordContent(resourceRecord.qtype, resourceRecord.content);
+      }
+      catch (std::exception& e) {
+        // makeApiRecordContent may throw an exception if the backend data
+        // is not well-formed (e.g. corrupted bind zone file).
+        // The exception gets caught here and rethrown as ApiException in
+        // order to return a 422 error code with a (hopefully) useful error
+        // message instead of a 500 error.
+        throw ApiException("Ill-formed record contents found for " + resourceRecord.qname.toString() + ": " + e.what());
+      }
       auto object = Json::object{
         {"object_type", "record"},
         {"name", resourceRecord.qname.toString()},
         {"type", resourceRecord.qtype.toString()},
         {"ttl", (double)resourceRecord.ttl},
         {"disabled", resourceRecord.disabled},
-        {"content", makeApiRecordContent(resourceRecord.qtype, resourceRecord.content)}};
+        {"content", content}};
       if (resourceRecord.last_modified != 0) {
         object["modified_at"] = (double)resourceRecord.last_modified;
       }