]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
dnsdist: Add an option to write `grepq`'s output to a file
authorRemi Gacogne <remi.gacogne@powerdns.com>
Mon, 27 Mar 2023 15:03:43 +0000 (17:03 +0200)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Mon, 27 Mar 2023 15:03:43 +0000 (17:03 +0200)
pdns/dnsdist-console.cc
pdns/dnsdist-lua-inspection.cc
pdns/dnsdistdist/docs/reference/config.rst

index 86647243ad4e806351f366dcfb84e822c9282d36..7eff465774b04cb3bf1849c9bc7e91d34140333e 100644 (file)
@@ -548,7 +548,7 @@ const std::vector<ConsoleKeyword> g_consoleKeywords{
   { "getTLSFrontend", true, "n", "returns the TLS frontend with index n" },
   { "getTLSFrontendCount", true, "", "returns the number of DoT listeners" },
   { "getVerbose", true, "", "get whether log messages at the verbose level will be logged" },
-  { "grepq", true, "Netmask|DNS Name|100ms|{\"::1\", \"powerdns.com\", \"100ms\"} [, n]", "shows the last n queries and responses matching the specified client address or range (Netmask), or the specified DNS Name, or slower than 100ms" },
+  { "grepq", true, "Netmask|DNS Name|100ms|{\"::1\", \"powerdns.com\", \"100ms\"} [, n] [,options]", "shows the last n queries and responses matching the specified client address or range (Netmask), or the specified DNS Name, or slower than 100ms" },
   { "hashPassword", true, "password [, workFactor]", "Returns a hashed and salted version of the supplied password, usable with 'setWebserverConfig()'"},
   { "HTTPHeaderRule", true, "name, regex", "matches DoH queries with a HTTP header 'name' whose content matches the regular expression 'regex'"},
   { "HTTPPathRegexRule", true, "regex", "matches DoH queries whose HTTP path matches 'regex'"},
index 86b3e0b5918c324128aa1cd99a2ff2c818ff50d7..b1f7ac0e095a6334977c86a821e56de68e5f8c4f 100644 (file)
@@ -19,6 +19,8 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
+#include <fcntl.h>
+
 #include "dnsdist.hh"
 #include "dnsdist-lua.hh"
 #include "dnsdist-dynblocks.hh"
@@ -408,11 +410,28 @@ void setupLuaInspection(LuaContext& luaCtx)
       }
     });
 
-  luaCtx.writeFunction("grepq", [](LuaTypeOrArrayOf<std::string> inp, boost::optional<unsigned int> limit) {
+  luaCtx.writeFunction("grepq", [](LuaTypeOrArrayOf<std::string> inp, boost::optional<unsigned int> limit, boost::optional<LuaAssociativeTable<std::string>> options) {
       setLuaNoSideEffect();
       boost::optional<Netmask>  nm;
       boost::optional<DNSName> dn;
       int msec=-1;
+      std::unique_ptr<FILE, decltype(&fclose)> outputFile{nullptr, fclose};
+
+      if (options) {
+        if (options->count("outputFile")) {
+          const std::string& outputFileName = options->at("outputFile");
+          int fd = open(outputFileName.c_str(), O_CREAT | O_EXCL | O_WRONLY, 0600);
+          if (fd < 0) {
+            g_outputBuffer = "Error opening dump file for writing: " + stringerror() + "\n";
+            return;
+          }
+          outputFile = std::unique_ptr<FILE, decltype(&fclose)>(fdopen(fd, "w"), fclose);
+          if (outputFile == nullptr) {
+            g_outputBuffer = "Error opening dump file for writing: " + stringerror() + "\n";
+            return;
+          }
+        }
+      }
 
       vector<string> vec;
       auto str=boost::get<string>(&inp);
@@ -477,8 +496,13 @@ void setupLuaInspection(LuaContext& luaCtx)
 
       std::multimap<struct timespec, string> out;
 
-      boost::format      fmt("%-7.1f %-47s %-12s %-12s %-5d %-25s %-5s %-6.1f %-2s %-2s %-2s %-s\n");
-      g_outputBuffer+= (fmt % "Time" % "Client" % "Protocol" % "Server" % "ID" % "Name" % "Type" % "Lat." % "TC" % "RD" % "AA" % "Rcode").str();
+      boost::format        fmt("%-7.1f %-47s %-12s %-12s %-5d %-25s %-5s %-6.1f %-2s %-2s %-2s %-s\n");
+      if (!outputFile) {
+        g_outputBuffer += (fmt % "Time" % "Client" % "Protocol" % "Server" % "ID" % "Name" % "Type" % "Lat." % "TC" % "RD" % "AA" % "Rcode").str();
+      }
+      else {
+        fprintf(outputFile.get(), "%s", (fmt % "Time" % "Client" % "Protocol" % "Server" % "ID" % "Name" % "Type" % "Lat." % "TC" % "RD" % "AA" % "Rcode").str().c_str());
+      }
 
       if(msec==-1) {
         for(const auto& c : qr) {
@@ -556,8 +580,13 @@ void setupLuaInspection(LuaContext& luaCtx)
         }
       }
 
-      for(const auto& p : out) {
-        g_outputBuffer+=p.second;
+      for (const auto& p : out) {
+        if (!outputFile) {
+          g_outputBuffer += p.second;
+        }
+        else {
+          fprintf(outputFile.get(), "%s", p.second.c_str());
+        }
       }
     });
 
index d7c41e7317e6d16035d19ac082ce2a5b837e4faf..d67c0b1c415347c9fec1297ac71d9852a8fb388b 100644 (file)
@@ -1138,8 +1138,11 @@ Status, Statistics and More
 
   :param int top: How many rules to return. Default is 10.
 
-.. function:: grepq(selector[, num])
-              grepq(selectors[, num])
+.. function:: grepq(selector[, num [, options]])
+              grepq(selectors[, num [, options]])
+
+  .. versionchanged:: 1.9.0
+    ``options`` optional parameter table added.
 
   Prints the last ``num`` queries and responses matching ``selector`` or ``selectors``.
   Queries and responses are accounted in separate ring buffers, and answers from the packet cache are not stored in the response ring buffer.
@@ -1154,6 +1157,11 @@ Status, Statistics and More
   :param str selector: Select queries based on this property.
   :param {str} selectors: A lua table of selectors. Only queries matching all selectors are shown
   :param int num: Show a maximum of ``num`` recent queries+responses, default is 10.
+  :param table options: A table with key: value pairs with options described below.
+
+  Options:
+
+  * ``outputFile=path``: string - Write the output of the command to the supplied file, instead of the standard output.
 
 .. function:: setVerbose(verbose)