]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
rec: OTTrace REST management: list all conditions
authorOtto Moerbeek <otto.moerbeek@open-xchange.com>
Tue, 13 Jan 2026 14:30:53 +0000 (15:30 +0100)
committerOtto Moerbeek <otto.moerbeek@open-xchange.com>
Tue, 13 Jan 2026 16:01:54 +0000 (17:01 +0100)
Signed-off-by: Otto Moerbeek <otto.moerbeek@open-xchange.com>
pdns/dnsname.hh
pdns/recursordist/rec-main.cc
pdns/recursordist/rec-main.hh
pdns/recursordist/rec-rust-lib/rust/src/bridge.hh
pdns/recursordist/rec-rust-lib/rust/src/web.rs
pdns/recursordist/rec-web-stubs.hh
pdns/recursordist/rec_channel_rec.cc
pdns/recursordist/ws-recursor.cc

index e5fde7877855b71add79eefbf2e3826d29fdf252..db78e2866167d0bf6820ced8bfd4bef00247c0e1 100644 (file)
@@ -725,6 +725,15 @@ struct SuffixMatchNode
       return ret;
     }
 
+  std::vector<DNSName> toVector() const
+    {
+      std::vector<DNSName> ret;
+      for (const auto& n : d_nodes) {
+        ret.emplace_back(n);
+      }
+      return ret;
+    }
+
   private:
     mutable std::set<DNSName> d_nodes; // Only used for string generation
 };
index 05f04e31e508d2f20a03ca5bc2ba6413e4d52477..b06b482d25bc3f8d649840d64a8aa46329777eb4 100644 (file)
@@ -98,7 +98,6 @@ uint32_t g_disthashseed;
 bool g_useIncomingECS;
 static shared_ptr<NetmaskGroup> g_initialProxyProtocolACL;
 static shared_ptr<std::set<ComboAddress>> g_initialProxyProtocolExceptions;
-static shared_ptr<OpenTelemetryTraceConditions> g_initialOpenTelemetryConditions; // XXX shared ptr needed?
 std::optional<ComboAddress> g_dns64Prefix{std::nullopt};
 DNSName g_dns64PrefixReverse;
 unsigned int g_maxChainLength;
@@ -106,6 +105,7 @@ LockGuarded<std::shared_ptr<SyncRes::domainmap_t>> g_initialDomainMap; // new th
 LockGuarded<std::shared_ptr<NetmaskGroup>> g_initialAllowFrom; // new thread needs to be setup with this
 LockGuarded<std::shared_ptr<NetmaskGroup>> g_initialAllowNotifyFrom; // new threads need this to be setup
 LockGuarded<std::shared_ptr<notifyset_t>> g_initialAllowNotifyFor; // new threads need this to be setup
+LockGuarded<std::shared_ptr<OpenTelemetryTraceConditions>> g_initialOpenTelemetryConditions; // new threads need this to be setup
 static time_t s_statisticsInterval;
 static std::atomic<uint32_t> s_counter;
 int g_argc;
@@ -129,7 +129,6 @@ std::vector<RecThreadInfo> RecThreadInfo::s_threadInfos;
 std::unique_ptr<ProxyMapping> g_proxyMapping; // new threads needs this to be setup
 thread_local std::unique_ptr<ProxyMapping> t_proxyMapping;
 
-std::unique_ptr<OpenTelemetryTraceConditions> g_OTConditions; // new threads needs this to be setup
 thread_local std::unique_ptr<OpenTelemetryTraceConditions> t_OTConditions;
 
 bool RecThreadInfo::s_weDistributeQueries; // if true, 1 or more threads listen on the incoming query sockets and distribute them to workers
@@ -2790,8 +2789,9 @@ static void recursorThread()
       else {
         t_proxyMapping = nullptr;
       }
-      if (g_OTConditions) {
-        t_OTConditions = make_unique<OpenTelemetryTraceConditions>(*g_OTConditions);
+      auto lock = g_initialOpenTelemetryConditions.lock();
+      if (*lock) {
+        t_OTConditions = make_unique<OpenTelemetryTraceConditions>(**lock);
       }
       else {
         t_OTConditions = nullptr;
index abbacf33005cefda56d17adc5b257106913b5f23..7657127f6aa7c2cdc1f29b2696adf6bb27ec7ce0 100644 (file)
@@ -238,6 +238,7 @@ extern LockGuarded<std::shared_ptr<SyncRes::domainmap_t>> g_initialDomainMap; //
 extern LockGuarded<std::shared_ptr<NetmaskGroup>> g_initialAllowFrom; // new thread needs to be setup with this
 extern LockGuarded<std::shared_ptr<NetmaskGroup>> g_initialAllowNotifyFrom; // new threads need this to be setup
 extern LockGuarded<std::shared_ptr<notifyset_t>> g_initialAllowNotifyFor; // new threads need this to be setup
+extern LockGuarded<std::shared_ptr<OpenTelemetryTraceConditions>> g_initialOpenTelemetryConditions; // new threads need this to be set
 extern thread_local std::shared_ptr<Regex> t_traceRegex;
 extern thread_local FDWrapper t_tracefd;
 extern string g_programname;
index 0710ea48d12159b9993ef20e154f5a06f7244969..c18f0866a1704dcdff3b28324bda209ea6b949cb 100644 (file)
@@ -98,4 +98,6 @@ void apiServerSearchData(const Request& rustRequest, Response& rustResponse);
 void apiServerZoneDetailGET(const Request& rustRequest, Response& rustResponse);
 void apiServerZoneDetailPUT(const Request& rustRequest, Response& rustResponse);
 void apiServerZoneDetailDELETE(const Request& rustRequest, Response& rustResponse);
+void apiServerOTConditionsGET(const Request& rustRequest, Response& rustResponse);
+void apiServerOTConditionDetailGET(const Request& rustRequest, Response& rustResponse);
 }
index c95cdd5f0df01749b76fa39f84a1fa88e37b0007..5d043d5ab5e6df4e507b511e36c48e1b194e673d 100644 (file)
@@ -399,6 +399,20 @@ fn matcher(
         (&Method::GET, ["api", "v1"]) => *apifunc = Some(rustweb::apiDiscoveryV1),
         (&Method::GET, ["api"]) => *apifunc = Some(rustweb::apiDiscovery),
         (&Method::GET, ["metrics"]) => *rawfunc = Some(rustweb::prometheusMetrics),
+        (&Method::GET, ["api", "v1", "servers", "localhost", "otconditions"]) => {
+            *apifunc = Some(rustweb::apiServerOTConditionsGET);
+        }
+        (&Method::GET, ["api", "v1", "servers", "localhost", "otconditions", id]) => {
+            let decoded = form_urlencoded::parse(id.as_bytes());
+            // decoded should contain a single key without value
+            if let Some(kv) = decoded.last() {
+                request.parameters.push(rustweb::KeyValue {
+                    key: String::from("id"),
+                    value: kv.0.to_string(),
+                });
+            }
+            *apifunc = Some(rustweb::apiServerOTConditionDetailGET)
+        }
         _ => *filefunc = Some(file),
     }
 }
@@ -1146,6 +1160,8 @@ mod rustweb {
         fn apiServerZoneDetailPUT(request: &Request, response: &mut Response) -> Result<()>;
         fn apiServerZonesGET(request: &Request, response: &mut Response) -> Result<()>;
         fn apiServerZonesPOST(requst: &Request, response: &mut Response) -> Result<()>;
+        fn apiServerOTConditionsGET(request: &Request, response: &mut Response) -> Result<()>;
+        fn apiServerOTConditionDetailGET(request: &Request, response: &mut Response) -> Result<()>;
         fn jsonstat(request: &Request, response: &mut Response) -> Result<()>;
         fn prometheusMetrics(request: &Request, response: &mut Response) -> Result<()>;
         fn serveStuff(request: &Request, response: &mut Response) -> Result<()>;
index a2be0482e254ba5b0f02ef384d05bc567bd2ad15..cedc574626f7cab1d516ac011f50e01da311a14f 100644 (file)
@@ -26,6 +26,8 @@ WRAPPER(apiServerZoneDetailGET)
 WRAPPER(apiServerZoneDetailPUT)
 WRAPPER(apiServerZonesGET)
 WRAPPER(apiServerZonesPOST)
+WRAPPER(apiServerOTConditionsGET)
+WRAPPER(apiServerOTConditionDetailGET)
 WRAPPER(jsonstat)
 WRAPPER(prometheusMetrics)
 WRAPPER(serveStuff)
index 8d9505101a75de2ca99c4fc8cd3f013af8846129..40cc4c022a130435da77271be6f3e8f9bec9cdb3 100644 (file)
@@ -2123,14 +2123,15 @@ RecursorControlChannel::Answer luaconfig(bool broadcast)
     lci = g_luaconfs.getCopy();
     if (broadcast) {
       startLuaConfigDelayedThreads(lci, lci.generation);
+
+      *g_initialOpenTelemetryConditions.lock() = conditions.empty() ? nullptr : std::make_unique<OpenTelemetryTraceConditions>(conditions);
       broadcastFunction([pmap = std::move(proxyMapping)] { return pleaseSupplantProxyMapping(pmap); });
       broadcastFunction([conds = std::move(conditions)] { return pleaseSupplantOTConditions(conds); });
     }
     else {
-      extern std::unique_ptr<OpenTelemetryTraceConditions> g_OTConditions;
       // Initial proxy mapping
       g_proxyMapping = proxyMapping.empty() ? nullptr : std::make_unique<ProxyMapping>(proxyMapping);
-      g_OTConditions = conditions.empty() ? nullptr : std::make_unique<OpenTelemetryTraceConditions>(conditions);
+      *g_initialOpenTelemetryConditions.lock() = conditions.empty() ? nullptr : std::make_unique<OpenTelemetryTraceConditions>(conditions);
     }
     TCPOutConnectionManager::setupOutgoingTLSConfigTables(settings);
 
index 304be11fd69312e1b87b04e5ff53bcc03ddfab96..ff56da38318b7aba370b2127644b877090c23d23 100644 (file)
@@ -543,6 +543,90 @@ static void apiServerRPZStats(HttpRequest* /* req */, HttpResponse* resp)
   resp->setJsonBody(ret);
 }
 
+static void apiServerOTConditionsGET(HttpRequest* /* req */, HttpResponse* resp)
+{
+  Json::array doc;
+  auto lock = g_initialOpenTelemetryConditions.lock();
+  if (*lock) {
+    for (const auto& condition : **lock) {
+      Json::object object{
+        {"acl", condition.first.toString()},
+        {"edns_option_required", condition.second.d_edns_option_required},
+        {"traceid_only", condition.second.d_traceid_only},
+      };
+      if (condition.second.d_qid) {
+        object.emplace("qid", *condition.second.d_qid);
+      }
+      if (condition.second.d_qnames) {
+        Json::array array;
+        for (const auto& name : condition.second.d_qnames->toVector()) {
+          array.emplace_back(name.toString());
+        }
+        object.emplace("qnames", array);
+      }
+      if (condition.second.d_qtypes) {
+        Json::array array;
+        for (const auto& name : *condition.second.d_qtypes) {
+          array.emplace_back(name.toString());
+        }
+        object.emplace("qtypes", array);
+      }
+
+      doc.emplace_back(std::move(object));
+    }
+  }
+  resp->setJsonBody(doc);
+}
+
+static void fillOTCondition(const Netmask& netmask, HttpResponse* resp)
+{
+  Json::array doc;
+  auto lock = g_initialOpenTelemetryConditions.lock();
+  if (*lock) {
+    auto condition = (*lock)->lookup(netmask);
+    if (condition != nullptr) {
+      Json::object object{
+        {"acl", condition->first.toString()},
+        {"edns_option_required", condition->second.d_edns_option_required},
+        {"traceid_only", condition->second.d_traceid_only},
+      };
+      if (condition->second.d_qid) {
+        object.emplace("qid", *condition->second.d_qid);
+      }
+      if (condition->second.d_qnames) {
+        Json::array array;
+        for (const auto& name : condition->second.d_qnames->toVector()) {
+          array.emplace_back(name.toString());
+        }
+        object.emplace("qnames", array);
+      }
+      if (condition->second.d_qtypes) {
+        Json::array array;
+        for (const auto& name : *condition->second.d_qtypes) {
+          array.emplace_back(name.toString());
+        }
+        object.emplace("qtypes", array);
+      }
+      resp->setJsonBody(object);
+      return;
+    }
+  }
+  throw ApiException("Could not find otcondition '" + netmask.toString() + "'");
+}
+
+static void apiServerOTConditionDetailGET(HttpRequest* req, HttpResponse* resp)
+{
+  try {
+    cerr << req->parameters["id"] << endl;
+    Netmask netmask{req->parameters["id"]};
+    cerr << netmask.toString() << endl;
+    fillOTCondition(netmask, resp);
+  }
+  catch (NetmaskException& ex) {
+    throw ApiException("Could not parse netmask");
+  }
+}
+
 static void prometheusMetrics(HttpRequest* /* req */, HttpResponse* resp)
 {
   static MetricDefinitionStorage s_metricDefinitions;
@@ -1115,6 +1199,8 @@ WRAPPER(apiServerZoneDetailGET)
 WRAPPER(apiServerZoneDetailPUT)
 WRAPPER(apiServerZonesGET)
 WRAPPER(apiServerZonesPOST)
+WRAPPER(apiServerOTConditionsGET)
+WRAPPER(apiServerOTConditionDetailGET)
 WRAPPER(prometheusMetrics)
 WRAPPER(serveStuff)