]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
LogResponseAction
authorphonedph1 <phoned@gmail.com>
Fri, 20 Dec 2019 17:05:11 +0000 (17:05 +0000)
committerphonedph1 <phoned@gmail.com>
Fri, 20 Dec 2019 17:05:11 +0000 (17:05 +0000)
pdns/dnsdist-console.cc
pdns/dnsdist-lua-actions.cc

index c6a9b8132abac0a0607d7dcd44f7309ba2713fa7..e53cf8d6117d1b5253eaa94322fddd1adaef0e8b 100644 (file)
@@ -422,6 +422,7 @@ const std::vector<ConsoleKeyword> g_consoleKeywords{
   { "KeyValueStoreLookupRule", true, "kvs, lookupKey", "matches queries if the key is found in the specified Key Value store" },
   { "leastOutstanding", false, "", "Send traffic to downstream server with least outstanding queries, with the lowest 'order', and within that the lowest recent latency"},
   { "LogAction", true, "[filename], [binary], [append], [buffered]", "Log a line for each query, to the specified file if any, to the console (require verbose) otherwise. When logging to a file, the `binary` optional parameter specifies whether we log in binary form (default) or in textual form, the `append` optional parameter specifies whether we open the file for appending or truncate each time (default), and the `buffered` optional parameter specifies whether writes to the file are buffered (default) or not." },
+  { "LogResponseAction", true, "[filename], [append], [buffered]", "Log a line for each response, to the specified file if any, to the console (require verbose) otherwise. The `append` optional parameter specifies whether we open the file for appending or truncate each time (default), and the `buffered` optional parameter specifies whether writes to the file are buffered (default) or not." },
   { "LuaAction", true, "function", "Invoke a Lua function that accepts a DNSQuestion" },
   { "LuaResponseAction", true, "function", "Invoke a Lua function that accepts a DNSResponse" },
   { "MacAddrAction", true, "option", "Add the source MAC address to the query as EDNS0 option option. This action is currently only supported on Linux. Subsequent rules are processed after this action" },
index 8312af301c96931635c910c8c4f79c294257cd84..0c3ae18a078ed4a9b890089c47d64f44efa3ea7c 100644 (file)
@@ -629,6 +629,64 @@ private:
   bool d_includeTimestamp{false};
 };
 
+class LogResponseAction : public DNSResponseAction, public boost::noncopyable
+{
+public:
+  LogResponseAction(): d_fp(nullptr, fclose)
+  {
+  }
+
+  LogResponseAction(const std::string& str, bool append=false, bool buffered=true, bool verboseOnly=true, bool includeTimestamp=false): d_fname(str), d_verboseOnly(verboseOnly), d_includeTimestamp(includeTimestamp)
+  {
+    if(str.empty())
+      return;
+    if(append)
+      d_fp = std::unique_ptr<FILE, int(*)(FILE*)>(fopen(str.c_str(), "a+"), fclose);
+    else
+      d_fp = std::unique_ptr<FILE, int(*)(FILE*)>(fopen(str.c_str(), "w"), fclose);
+    if(!d_fp)
+      throw std::runtime_error("Unable to open file '"+str+"' for logging: "+stringerror());
+    if(!buffered)
+      setbuf(d_fp.get(), 0);
+  }
+
+  DNSResponseAction::Action operator()(DNSResponse* dr, std::string* ruleresult) const override
+  {
+    if (!d_fp) {
+      if (!d_verboseOnly || g_verbose) {
+        if (d_includeTimestamp) {
+          infolog("[%u.%u] Answer to %s for %s %s (%s) with id %d", static_cast<unsigned long long>(dr->queryTime->tv_sec), static_cast<unsigned long>(dr->queryTime->tv_nsec), dr->remote->toStringWithPort(), dr->qname->toString(), QType(dr->qtype).getName(), RCode::to_s(dr->dh->rcode), dr->dh->id);
+        }
+        else {
+          infolog("Answer to %s for %s %s (%s) with id %d", dr->remote->toStringWithPort(), dr->qname->toString(), QType(dr->qtype).getName(), RCode::to_s(dr->dh->rcode), dr->dh->id);
+        }
+      }
+    }
+    else {
+      if (d_includeTimestamp) {
+        fprintf(d_fp.get(), "[%llu.%lu] Answer to %s for %s %s (%s) with id %d\n", static_cast<unsigned long long>(dr->queryTime->tv_sec), static_cast<unsigned long>(dr->queryTime->tv_nsec), dr->remote->toStringWithPort().c_str(), dr->qname->toString().c_str(), QType(dr->qtype).getName().c_str(), RCode::to_s(dr->dh->rcode).c_str(), dr->dh->id);
+      }
+      else {
+        fprintf(d_fp.get(), "Answer to %s for %s %s (%s) with id %d\n", dr->remote->toStringWithPort().c_str(), dr->qname->toString().c_str(), QType(dr->qtype).getName().c_str(), RCode::to_s(dr->dh->rcode).c_str(), dr->dh->id);
+      }
+    }
+    return Action::None;
+  }
+
+  std::string toString() const override
+  {
+    if (!d_fname.empty()) {
+      return "log to " + d_fname;
+    }
+    return "log";
+  }
+private:
+  std::string d_fname;
+  std::unique_ptr<FILE, int(*)(FILE*)> d_fp{nullptr, fclose};
+  bool d_verboseOnly{true};
+  bool d_includeTimestamp{false};
+};
+
 
 class DisableValidationAction : public DNSAction
 {
@@ -1324,6 +1382,10 @@ void setupLuaActions()
       return std::shared_ptr<DNSAction>(new LogAction(fname ? *fname : "", binary ? *binary : true, append ? *append : false, buffered ? *buffered : false, verboseOnly ? *verboseOnly : true, includeTimestamp ? *includeTimestamp : false));
     });
 
+  g_lua.writeFunction("LogResponseAction", [](boost::optional<std::string> fname, boost::optional<bool> append, boost::optional<bool> buffered, boost::optional<bool> verboseOnly, boost::optional<bool> includeTimestamp) {
+      return std::shared_ptr<DNSResponseAction>(new LogResponseAction(fname ? *fname : "", append ? *append : false, buffered ? *buffered : false, verboseOnly ? *verboseOnly : true, includeTimestamp ? *includeTimestamp : false));
+    });
+
   g_lua.writeFunction("RCodeAction", [](uint8_t rcode) {
       return std::shared_ptr<DNSAction>(new RCodeAction(rcode));
     });