]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
dnsdist: Stop using Actions for regular processing
authorRemi Gacogne <remi.gacogne@powerdns.com>
Tue, 24 Dec 2024 11:50:41 +0000 (12:50 +0100)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Thu, 16 Jan 2025 08:50:23 +0000 (09:50 +0100)
pdns/dnsdistdist/Makefile.am
pdns/dnsdistdist/dnsdist-lua-actions.cc
pdns/dnsdistdist/dnsdist-lua-bindings-dnsquestion.cc
pdns/dnsdistdist/dnsdist-lua-ffi.cc
pdns/dnsdistdist/dnsdist-lua.hh
pdns/dnsdistdist/dnsdist-self-answers.cc [new file with mode: 0644]
pdns/dnsdistdist/dnsdist-self-answers.hh [new file with mode: 0644]
pdns/dnsdistdist/dnsdist.cc
pdns/dnsdistdist/test-dnsdist-lua-ffi.cc
pdns/dnsdistdist/test-dnsdistlbpolicies_cc.cc

index 3ab92cb6d388eadee43b46e3e2a01dc5b3aefe33..8a881c8b43e5d830bd2c2fb5dd7067ca71437625 100644 (file)
@@ -205,6 +205,7 @@ dnsdist_SOURCES = \
        dnsdist-rule-chains.cc dnsdist-rule-chains.hh \
        dnsdist-rules.cc dnsdist-rules.hh \
        dnsdist-secpoll.cc dnsdist-secpoll.hh \
+       dnsdist-self-answers.cc dnsdist-self-answers.hh \
        dnsdist-session-cache.cc dnsdist-session-cache.hh \
        dnsdist-snmp.cc dnsdist-snmp.hh \
        dnsdist-svc.cc dnsdist-svc.hh \
@@ -312,6 +313,7 @@ testrunner_SOURCES = \
        dnsdist-rings.cc dnsdist-rings.hh \
        dnsdist-rule-chains.cc dnsdist-rule-chains.hh \
        dnsdist-rules.cc dnsdist-rules.hh \
+       dnsdist-self-answers.cc dnsdist-self-answers.hh \
        dnsdist-session-cache.cc dnsdist-session-cache.hh \
        dnsdist-svc.cc dnsdist-svc.hh \
        dnsdist-tcp-downstream.cc \
index 17bc184dc6b15a2bf1e57767583d0b06c1422bd9..24c13b4482438946b61b27fb0d63cc9766af54bd 100644 (file)
@@ -33,6 +33,7 @@
 #include "dnsdist-proxy-protocol.hh"
 #include "dnsdist-kvs.hh"
 #include "dnsdist-rule-chains.hh"
+#include "dnsdist-self-answers.hh"
 #include "dnsdist-snmp.hh"
 #include "dnsdist-svc.hh"
 
@@ -411,8 +412,8 @@ private:
 class RCodeAction : public DNSAction
 {
 public:
-  RCodeAction(uint8_t rcode) :
-    d_rcode(rcode) {}
+  RCodeAction(uint8_t rcode, dnsdist::ResponseConfig responseConfig) :
+    d_responseConfig(responseConfig), d_rcode(rcode) {}
   DNSAction::Action operator()(DNSQuestion* dnsquestion, std::string* ruleresult) const override
   {
     dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsquestion->getMutableData(), [this](dnsheader& header) {
@@ -427,10 +428,6 @@ public:
   {
     return "set rcode " + std::to_string(d_rcode);
   }
-  [[nodiscard]] dnsdist::ResponseConfig& getResponseConfig()
-  {
-    return d_responseConfig;
-  }
 
 private:
   dnsdist::ResponseConfig d_responseConfig;
@@ -440,8 +437,8 @@ private:
 class ERCodeAction : public DNSAction
 {
 public:
-  ERCodeAction(uint8_t rcode) :
-    d_rcode(rcode) {}
+  ERCodeAction(uint8_t rcode, dnsdist::ResponseConfig responseConfig) :
+    d_responseConfig(responseConfig), d_rcode(rcode) {}
   DNSAction::Action operator()(DNSQuestion* dnsquestion, std::string* ruleresult) const override
   {
     dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsquestion->getMutableData(), [this](dnsheader& header) {
@@ -457,10 +454,6 @@ public:
   {
     return "set ercode " + ERCode::to_s(d_rcode);
   }
-  [[nodiscard]] dnsdist::ResponseConfig& getResponseConfig()
-  {
-    return d_responseConfig;
-  }
 
 private:
   dnsdist::ResponseConfig d_responseConfig;
@@ -470,7 +463,8 @@ private:
 class SpoofSVCAction : public DNSAction
 {
 public:
-  SpoofSVCAction(const LuaArray<SVCRecordParameters>& parameters)
+  SpoofSVCAction(const LuaArray<SVCRecordParameters>& parameters, dnsdist::ResponseConfig responseConfig) :
+    d_responseConfig(responseConfig)
   {
     d_payloads.reserve(parameters.size());
 
@@ -506,11 +500,6 @@ public:
     return "spoof SVC record ";
   }
 
-  [[nodiscard]] dnsdist::ResponseConfig& getResponseConfig()
-  {
-    return d_responseConfig;
-  }
-
 private:
   dnsdist::ResponseConfig d_responseConfig;
   std::vector<std::vector<uint8_t>> d_payloads{};
@@ -884,177 +873,101 @@ private:
 std::atomic<uint64_t> LuaFFIPerThreadResponseAction::s_functionsCounter = 0;
 thread_local std::map<uint64_t, LuaFFIPerThreadResponseAction::PerThreadState> LuaFFIPerThreadResponseAction::t_perThreadStates;
 
-thread_local std::default_random_engine SpoofAction::t_randomEngine;
-
-DNSAction::Action SpoofAction::operator()(DNSQuestion* dnsquestion, std::string* ruleresult) const
+class SpoofAction : public DNSAction
 {
-  uint16_t qtype = dnsquestion->ids.qtype;
-  // do we even have a response?
-  if (d_cname.empty() && d_rawResponses.empty() &&
-      // make sure pre-forged response is greater than sizeof(dnsheader)
-      (d_raw.size() < sizeof(dnsheader)) && d_types.count(qtype) == 0) {
-    return Action::None;
-  }
-
-  if (d_raw.size() >= sizeof(dnsheader)) {
-    auto questionId = dnsquestion->getHeader()->id;
-    dnsquestion->getMutableData() = d_raw;
-    dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsquestion->getMutableData(), [questionId](dnsheader& header) {
-      header.id = questionId;
-      return true;
-    });
-    return Action::HeaderModify;
-  }
-  std::vector<ComboAddress> addrs = {};
-  std::vector<std::string> rawResponses = {};
-  unsigned int totrdatalen = 0;
-  size_t numberOfRecords = 0;
-  if (!d_cname.empty()) {
-    qtype = QType::CNAME;
-    totrdatalen += d_cname.getStorage().size();
-    numberOfRecords = 1;
-  }
-  else if (!d_rawResponses.empty()) {
-    rawResponses.reserve(d_rawResponses.size());
-    for (const auto& rawResponse : d_rawResponses) {
-      totrdatalen += rawResponse.size();
-      rawResponses.push_back(rawResponse);
-      ++numberOfRecords;
-    }
-    if (rawResponses.size() > 1) {
-      shuffle(rawResponses.begin(), rawResponses.end(), t_randomEngine);
-    }
-  }
-  else {
+public:
+  SpoofAction(const vector<ComboAddress>& addrs, const dnsdist::ResponseConfig& responseConfig) :
+    d_responseConfig(responseConfig), d_addrs(addrs)
+  {
     for (const auto& addr : d_addrs) {
-      if (qtype != QType::ANY && ((addr.sin4.sin_family == AF_INET && qtype != QType::A) || (addr.sin4.sin_family == AF_INET6 && qtype != QType::AAAA))) {
-        continue;
+      if (addr.isIPv4()) {
+        d_types.insert(QType::A);
+      }
+      else if (addr.isIPv6()) {
+        d_types.insert(QType::AAAA);
       }
-      totrdatalen += addr.sin4.sin_family == AF_INET ? sizeof(addr.sin4.sin_addr.s_addr) : sizeof(addr.sin6.sin6_addr.s6_addr);
-      addrs.push_back(addr);
-      ++numberOfRecords;
     }
-  }
 
-  if (addrs.size() > 1) {
-    shuffle(addrs.begin(), addrs.end(), t_randomEngine);
+    if (!d_addrs.empty()) {
+      d_types.insert(QType::ANY);
+    }
   }
 
-  unsigned int qnameWireLength = 0;
-  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
-  DNSName ignore(reinterpret_cast<const char*>(dnsquestion->getData().data()), dnsquestion->getData().size(), sizeof(dnsheader), false, nullptr, nullptr, &qnameWireLength);
-
-  if (dnsquestion->getMaximumSize() < (sizeof(dnsheader) + qnameWireLength + 4 + numberOfRecords * 12 /* recordstart */ + totrdatalen)) {
-    return Action::None;
+  SpoofAction(const DNSName& cname, const dnsdist::ResponseConfig& responseConfig) :
+    d_responseConfig(responseConfig), d_cname(cname)
+  {
   }
 
-  bool dnssecOK = false;
-  bool hadEDNS = false;
-  if (dnsdist::configuration::getCurrentRuntimeConfiguration().d_addEDNSToSelfGeneratedResponses && queryHasEDNS(*dnsquestion)) {
-    hadEDNS = true;
-    dnssecOK = ((dnsdist::getEDNSZ(*dnsquestion) & EDNS_HEADER_FLAG_DO) != 0);
+  SpoofAction(const PacketBuffer& rawresponse) :
+    d_raw(rawresponse)
+  {
   }
 
-  auto& data = dnsquestion->getMutableData();
-  data.resize(sizeof(dnsheader) + qnameWireLength + 4 + numberOfRecords * 12 /* recordstart */ + totrdatalen); // there goes your EDNS
-  uint8_t* dest = &(data.at(sizeof(dnsheader) + qnameWireLength + 4));
+  SpoofAction(const vector<std::string>& raws, std::optional<uint16_t> typeForAny, const dnsdist::ResponseConfig& responseConfig) :
+    d_responseConfig(responseConfig), d_rawResponses(raws), d_rawTypeForAny(typeForAny)
+  {
+  }
 
-  dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsquestion->getMutableData(), [this](dnsheader& header) {
-    header.qr = true; // for good measure
-    setResponseHeadersFromConfig(header, d_responseConfig);
-    header.ancount = 0;
-    header.arcount = 0; // for now, forget about your EDNS, we're marching over it
-    return true;
-  });
+  DNSAction::Action operator()(DNSQuestion* dnsquestion, string* ruleresult) const override;
 
-  uint32_t ttl = htonl(d_responseConfig.ttl);
-  uint16_t qclass = htons(dnsquestion->ids.qclass);
-  std::array<unsigned char, 12> recordstart = {
-    0xc0, 0x0c, // compressed name
-    0, 0, // QTYPE
-    0, 0, // QCLASS
-    0, 0, 0, 0, // TTL
-    0, 0 // rdata length
-  };
-  static_assert(recordstart.size() == 12, "sizeof(recordstart) must be equal to 12, otherwise the above check is invalid");
-  memcpy(&recordstart[4], &qclass, sizeof(qclass));
-  memcpy(&recordstart[6], &ttl, sizeof(ttl));
-
-  if (qtype == QType::CNAME) {
-    const auto& wireData = d_cname.getStorage(); // Note! This doesn't do compression!
-    uint16_t rdataLen = htons(wireData.length());
-    qtype = htons(qtype);
-    memcpy(&recordstart[2], &qtype, sizeof(qtype));
-    memcpy(&recordstart[10], &rdataLen, sizeof(rdataLen));
-
-    memcpy(dest, recordstart.data(), recordstart.size());
-    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
-    dest += recordstart.size();
-    memcpy(dest, wireData.c_str(), wireData.length());
-    dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsquestion->getMutableData(), [](dnsheader& header) {
-      header.ancount++;
-      return true;
-    });
-  }
-  else if (!rawResponses.empty()) {
-    if (qtype == QType::ANY && d_rawTypeForAny) {
-      qtype = *d_rawTypeForAny;
+  string toString() const override
+  {
+    string ret = "spoof in ";
+    if (!d_cname.empty()) {
+      ret += d_cname.toString() + " ";
+    }
+    if (d_rawResponses.size() > 0) {
+      ret += "raw bytes ";
+    }
+    else {
+      for (const auto& a : d_addrs)
+        ret += a.toString() + " ";
     }
-    qtype = htons(qtype);
-    for (const auto& rawResponse : rawResponses) {
-      uint16_t rdataLen = htons(rawResponse.size());
-      memcpy(&recordstart[2], &qtype, sizeof(qtype));
-      memcpy(&recordstart[10], &rdataLen, sizeof(rdataLen));
+    return ret;
+  }
 
-      memcpy(dest, recordstart.data(), sizeof(recordstart));
-      // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
-      dest += recordstart.size();
+private:
+  dnsdist::ResponseConfig d_responseConfig;
+  std::vector<ComboAddress> d_addrs;
+  std::unordered_set<uint16_t> d_types;
+  std::vector<std::string> d_rawResponses;
+  PacketBuffer d_raw;
+  DNSName d_cname;
+  std::optional<uint16_t> d_rawTypeForAny{};
+};
 
-      memcpy(dest, rawResponse.c_str(), rawResponse.size());
-      // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
-      dest += rawResponse.size();
+DNSAction::Action SpoofAction::operator()(DNSQuestion* dnsquestion, std::string* ruleresult) const
+{
+  uint16_t qtype = dnsquestion->ids.qtype;
+  // do we even have a response?
+  if (d_cname.empty() && d_rawResponses.empty() &&
+      // make sure pre-forged response is greater than sizeof(dnsheader)
+      (d_raw.size() < sizeof(dnsheader)) && d_types.count(qtype) == 0) {
+    return Action::None;
+  }
 
-      dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsquestion->getMutableData(), [](dnsheader& header) {
-        header.ancount++;
-        return true;
-      });
+  if (d_raw.size() >= sizeof(dnsheader)) {
+    dnsdist::self_answers::generateAnswerFromRawPacket(*dnsquestion, d_raw);
+    return Action::HeaderModify;
+  }
+
+  if (!d_cname.empty()) {
+    if (dnsdist::self_answers::generateAnswerFromCNAME(*dnsquestion, d_cname, d_responseConfig)) {
+      return Action::HeaderModify;
     }
   }
-  else {
-    for (const auto& addr : addrs) {
-      uint16_t rdataLen = htons(addr.sin4.sin_family == AF_INET ? sizeof(addr.sin4.sin_addr.s_addr) : sizeof(addr.sin6.sin6_addr.s6_addr));
-      qtype = htons(addr.sin4.sin_family == AF_INET ? QType::A : QType::AAAA);
-      memcpy(&recordstart[2], &qtype, sizeof(qtype));
-      memcpy(&recordstart[10], &rdataLen, sizeof(rdataLen));
-
-      memcpy(dest, recordstart.data(), recordstart.size());
-      // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
-      dest += sizeof(recordstart);
-
-      memcpy(dest,
-             // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
-             addr.sin4.sin_family == AF_INET ? reinterpret_cast<const void*>(&addr.sin4.sin_addr.s_addr) : reinterpret_cast<const void*>(&addr.sin6.sin6_addr.s6_addr),
-             addr.sin4.sin_family == AF_INET ? sizeof(addr.sin4.sin_addr.s_addr) : sizeof(addr.sin6.sin6_addr.s6_addr));
-      // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
-      dest += (addr.sin4.sin_family == AF_INET ? sizeof(addr.sin4.sin_addr.s_addr) : sizeof(addr.sin6.sin6_addr.s6_addr));
-      dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsquestion->getMutableData(), [](dnsheader& header) {
-        header.ancount++;
-        return true;
-      });
+  else if (!d_rawResponses.empty()) {
+    if (dnsdist::self_answers::generateAnswerFromRDataEntries(*dnsquestion, d_rawResponses, d_rawTypeForAny, d_responseConfig)) {
+      return Action::HeaderModify;
     }
   }
-
-  auto finalANCount = dnsquestion->getHeader()->ancount;
-  dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsquestion->getMutableData(), [finalANCount](dnsheader& header) {
-    header.ancount = htons(finalANCount);
-    return true;
-  });
-
-  if (hadEDNS) {
-    addEDNS(dnsquestion->getMutableData(), dnsquestion->getMaximumSize(), dnssecOK, dnsdist::configuration::getCurrentRuntimeConfiguration().d_payloadSizeSelfGenAnswers, 0);
+  else {
+    if (dnsdist::self_answers::generateAnswerFromIPAddresses(*dnsquestion, d_addrs, d_responseConfig)) {
+      return Action::HeaderModify;
+    }
   }
 
-  return Action::HeaderModify;
+  return Action::None;
 }
 
 class SetMacAddrAction : public DNSAction
@@ -2066,8 +1979,8 @@ private:
 class HTTPStatusAction : public DNSAction
 {
 public:
-  HTTPStatusAction(int code, PacketBuffer body, std::string contentType) :
-    d_body(std::move(body)), d_contentType(std::move(contentType)), d_code(code)
+  HTTPStatusAction(int code, PacketBuffer body, std::string contentType, dnsdist::ResponseConfig responseConfig) :
+    d_responseConfig(responseConfig), d_body(std::move(body)), d_contentType(std::move(contentType)), d_code(code)
   {
   }
 
@@ -2103,11 +2016,6 @@ public:
     return "return an HTTP status of " + std::to_string(d_code);
   }
 
-  [[nodiscard]] dnsdist::ResponseConfig& getResponseConfig()
-  {
-    return d_responseConfig;
-  }
-
 private:
   dnsdist::ResponseConfig d_responseConfig;
   PacketBuffer d_body;
@@ -2246,8 +2154,8 @@ public:
     uint32_t minimum;
   };
 
-  NegativeAndSOAAction(bool nxd, DNSName zone, uint32_t ttl, DNSName mname, DNSName rname, SOAParams params, bool soaInAuthoritySection) :
-    d_zone(std::move(zone)), d_mname(std::move(mname)), d_rname(std::move(rname)), d_ttl(ttl), d_params(params), d_nxd(nxd), d_soaInAuthoritySection(soaInAuthoritySection)
+  NegativeAndSOAAction(bool nxd, DNSName zone, uint32_t ttl, DNSName mname, DNSName rname, SOAParams params, bool soaInAuthoritySection, dnsdist::ResponseConfig responseConfig) :
+    d_responseConfig(responseConfig), d_zone(std::move(zone)), d_mname(std::move(mname)), d_rname(std::move(rname)), d_ttl(ttl), d_params(params), d_nxd(nxd), d_soaInAuthoritySection(soaInAuthoritySection)
   {
   }
 
@@ -2269,10 +2177,6 @@ public:
   {
     return std::string(d_nxd ? "NXD" : "NODATA") + " with SOA";
   }
-  [[nodiscard]] dnsdist::ResponseConfig& getResponseConfig()
-  {
-    return d_responseConfig;
-  }
 
 private:
   dnsdist::ResponseConfig d_responseConfig;
@@ -2448,12 +2352,14 @@ static void addAction(IdentifierT identifier, const luadnsrule_t& var, const std
 
 using responseParams_t = std::unordered_map<std::string, boost::variant<bool, uint32_t>>;
 
-static void parseResponseConfig(boost::optional<responseParams_t>& vars, dnsdist::ResponseConfig& config)
+static dnsdist::ResponseConfig parseResponseConfig(boost::optional<responseParams_t>& vars)
 {
+  dnsdist::ResponseConfig config;
   getOptionalValue<uint32_t>(vars, "ttl", config.ttl);
   getOptionalValue<bool>(vars, "aa", config.setAA);
   getOptionalValue<bool>(vars, "ad", config.setAD);
   getOptionalValue<bool>(vars, "ra", config.setRA);
+  return config;
 }
 
 // NOLINTNEXTLINE(readability-function-cognitive-complexity): this function declares Lua bindings, even with a good refactoring it will likely blow up the threshold
@@ -2574,25 +2480,23 @@ void setupLuaActions(LuaContext& luaCtx)
       }
     }
 
-    auto ret = std::shared_ptr<DNSAction>(new SpoofAction(addrs));
-    auto spoofaction = std::dynamic_pointer_cast<SpoofAction>(ret);
-    parseResponseConfig(vars, spoofaction->getResponseConfig());
+    auto responseConfig = parseResponseConfig(vars);
     checkAllParametersConsumed("SpoofAction", vars);
+    auto ret = std::shared_ptr<DNSAction>(new SpoofAction(addrs, responseConfig));
     return ret;
   });
 
   luaCtx.writeFunction("SpoofSVCAction", [](const LuaArray<SVCRecordParameters>& parameters, boost::optional<responseParams_t> vars) {
-    auto ret = std::shared_ptr<DNSAction>(new SpoofSVCAction(parameters));
-    auto spoofaction = std::dynamic_pointer_cast<SpoofSVCAction>(ret);
-    parseResponseConfig(vars, spoofaction->getResponseConfig());
+    auto responseConfig = parseResponseConfig(vars);
+    checkAllParametersConsumed("SpoofAction", vars);
+    auto ret = std::shared_ptr<DNSAction>(new SpoofSVCAction(parameters, responseConfig));
     return ret;
   });
 
   luaCtx.writeFunction("SpoofCNAMEAction", [](const std::string& cname, boost::optional<responseParams_t> vars) {
-    auto ret = std::shared_ptr<DNSAction>(new SpoofAction(DNSName(cname)));
-    auto spoofaction = std::dynamic_pointer_cast<SpoofAction>(ret);
-    parseResponseConfig(vars, spoofaction->getResponseConfig());
+    auto responseConfig = parseResponseConfig(vars);
     checkAllParametersConsumed("SpoofCNAMEAction", vars);
+    auto ret = std::shared_ptr<DNSAction>(new SpoofAction(DNSName(cname), responseConfig));
     return ret;
   });
 
@@ -2616,10 +2520,9 @@ void setupLuaActions(LuaContext& luaCtx)
     if (qtypeForAny > 0) {
       qtypeForAnyParam = static_cast<uint16_t>(qtypeForAny);
     }
-    auto ret = std::shared_ptr<DNSAction>(new SpoofAction(raws, qtypeForAnyParam));
-    auto spoofaction = std::dynamic_pointer_cast<SpoofAction>(ret);
-    parseResponseConfig(vars, spoofaction->getResponseConfig());
+    auto responseConfig = parseResponseConfig(vars);
     checkAllParametersConsumed("SpoofRawAction", vars);
+    auto ret = std::shared_ptr<DNSAction>(new SpoofAction(raws, qtypeForAnyParam, responseConfig));
     return ret;
   });
 
@@ -2627,7 +2530,8 @@ void setupLuaActions(LuaContext& luaCtx)
     if (len < sizeof(dnsheader)) {
       throw std::runtime_error(std::string("SpoofPacketAction: given packet len is too small"));
     }
-    auto ret = std::shared_ptr<DNSAction>(new SpoofAction(response.c_str(), len));
+    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
+    auto ret = std::shared_ptr<DNSAction>(new SpoofAction(PacketBuffer(response.data(), response.data() + len)));
     return ret;
   });
 
@@ -2716,18 +2620,16 @@ void setupLuaActions(LuaContext& luaCtx)
   });
 
   luaCtx.writeFunction("RCodeAction", [](uint8_t rcode, boost::optional<responseParams_t> vars) {
-    auto ret = std::shared_ptr<DNSAction>(new RCodeAction(rcode));
-    auto rca = std::dynamic_pointer_cast<RCodeAction>(ret);
-    parseResponseConfig(vars, rca->getResponseConfig());
+    auto responseConfig = parseResponseConfig(vars);
     checkAllParametersConsumed("RCodeAction", vars);
+    auto ret = std::shared_ptr<DNSAction>(new RCodeAction(rcode, responseConfig));
     return ret;
   });
 
   luaCtx.writeFunction("ERCodeAction", [](uint8_t rcode, boost::optional<responseParams_t> vars) {
-    auto ret = std::shared_ptr<DNSAction>(new ERCodeAction(rcode));
-    auto erca = std::dynamic_pointer_cast<ERCodeAction>(ret);
-    parseResponseConfig(vars, erca->getResponseConfig());
+    auto responseConfig = parseResponseConfig(vars);
     checkAllParametersConsumed("ERCodeAction", vars);
+    auto ret = std::shared_ptr<DNSAction>(new ERCodeAction(rcode, responseConfig));
     return ret;
   });
 
@@ -2914,10 +2816,9 @@ void setupLuaActions(LuaContext& luaCtx)
 
 #ifdef HAVE_DNS_OVER_HTTPS
   luaCtx.writeFunction("HTTPStatusAction", [](uint16_t status, std::string body, boost::optional<std::string> contentType, boost::optional<responseParams_t> vars) {
-    auto ret = std::shared_ptr<DNSAction>(new HTTPStatusAction(status, PacketBuffer(body.begin(), body.end()), contentType ? *contentType : ""));
-    auto hsa = std::dynamic_pointer_cast<HTTPStatusAction>(ret);
-    parseResponseConfig(vars, hsa->getResponseConfig());
+    auto responseConfig = parseResponseConfig(vars);
     checkAllParametersConsumed("HTTPStatusAction", vars);
+    auto ret = std::shared_ptr<DNSAction>(new HTTPStatusAction(status, PacketBuffer(body.begin(), body.end()), contentType ? *contentType : "", responseConfig));
     return ret;
   });
 #endif /* HAVE_DNS_OVER_HTTPS */
@@ -2933,6 +2834,7 @@ void setupLuaActions(LuaContext& luaCtx)
 #endif /* defined(HAVE_LMDB) || defined(HAVE_CDB) */
 
   luaCtx.writeFunction("NegativeAndSOAAction", [](bool nxd, const std::string& zone, uint32_t ttl, const std::string& mname, const std::string& rname, uint32_t serial, uint32_t refresh, uint32_t retry, uint32_t expire, uint32_t minimum, boost::optional<responseParams_t> vars) {
+    auto responseConfig = parseResponseConfig(vars);
     bool soaInAuthoritySection = false;
     getOptionalValue<bool>(vars, "soaInAuthoritySection", soaInAuthoritySection);
     NegativeAndSOAAction::SOAParams params{
@@ -2941,10 +2843,8 @@ void setupLuaActions(LuaContext& luaCtx)
       .retry = retry,
       .expire = expire,
       .minimum = minimum};
-    auto ret = std::shared_ptr<DNSAction>(new NegativeAndSOAAction(nxd, DNSName(zone), ttl, DNSName(mname), DNSName(rname), params, soaInAuthoritySection));
-    auto action = std::dynamic_pointer_cast<NegativeAndSOAAction>(ret);
-    parseResponseConfig(vars, action->getResponseConfig());
     checkAllParametersConsumed("NegativeAndSOAAction", vars);
+    auto ret = std::shared_ptr<DNSAction>(new NegativeAndSOAAction(nxd, DNSName(zone), ttl, DNSName(mname), DNSName(rname), params, soaInAuthoritySection, responseConfig));
     return ret;
   });
 
index 8863e0bb5da71d10372907ad61838f992f1f298d..8ae0d81704da7d24675b6bac41bf1e87b067d9a6 100644 (file)
@@ -25,6 +25,7 @@
 #include "dnsdist-ecs.hh"
 #include "dnsdist-internal-queries.hh"
 #include "dnsdist-lua.hh"
+#include "dnsdist-self-answers.hh"
 #include "dnsdist-snmp.hh"
 #include "dnsparser.hh"
 
@@ -254,6 +255,7 @@ void setupLuaBindingsDNSQuestion(LuaContext& luaCtx)
   });
 
   luaCtx.registerFunction<void (DNSQuestion::*)(const boost::variant<LuaArray<ComboAddress>, LuaArray<std::string>>&, boost::optional<uint16_t>)>("spoof", [](DNSQuestion& dnsQuestion, const boost::variant<LuaArray<ComboAddress>, LuaArray<std::string>>& response, boost::optional<uint16_t> typeForAny) {
+    dnsdist::ResponseConfig responseConfig;
     if (response.type() == typeid(LuaArray<ComboAddress>)) {
       std::vector<ComboAddress> data;
       auto responses = boost::get<LuaArray<ComboAddress>>(response);
@@ -261,9 +263,7 @@ void setupLuaBindingsDNSQuestion(LuaContext& luaCtx)
       for (const auto& resp : responses) {
         data.push_back(resp.second);
       }
-      std::string result;
-      SpoofAction tempSpoofAction(data);
-      tempSpoofAction(&dnsQuestion, &result);
+      dnsdist::self_answers::generateAnswerFromIPAddresses(dnsQuestion, data, responseConfig);
       return;
     }
     if (response.type() == typeid(LuaArray<std::string>)) {
@@ -273,9 +273,7 @@ void setupLuaBindingsDNSQuestion(LuaContext& luaCtx)
       for (const auto& resp : responses) {
         data.push_back(resp.second);
       }
-      std::string result;
-      SpoofAction tempSpoofAction(data, typeForAny ? *typeForAny : std::optional<uint16_t>());
-      tempSpoofAction(&dnsQuestion, &result);
+      dnsdist::self_answers::generateAnswerFromRDataEntries(dnsQuestion, data, typeForAny ? *typeForAny : std::optional<uint16_t>(), responseConfig);
       return;
     }
   });
index 4cff7f35bed1f2caade0c5d4369594b6b3f8a56f..54886c6b8d5078d42be25becfca541e5720e9e57 100644 (file)
@@ -32,6 +32,7 @@
 #include "dnsdist-lua.hh"
 #include "dnsdist-ecs.hh"
 #include "dnsdist-rings.hh"
+#include "dnsdist-self-answers.hh"
 #include "dnsdist-svc.hh"
 #include "dnsdist-snmp.hh"
 #include "dolog.hh"
@@ -644,9 +645,8 @@ void dnsdist_ffi_dnsquestion_send_trap(dnsdist_ffi_dnsquestion_t* dq, const char
 
 void dnsdist_ffi_dnsquestion_spoof_packet(dnsdist_ffi_dnsquestion_t* dq, const char* raw, size_t len)
 {
-  std::string result;
-  SpoofAction tempSpoofAction(raw, len);
-  tempSpoofAction(dq->dq, &result);
+  // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
+  dnsdist::self_answers::generateAnswerFromRawPacket(*dq->dq, PacketBuffer(raw, raw + len));
 }
 
 void dnsdist_ffi_dnsquestion_spoof_raw(dnsdist_ffi_dnsquestion_t* dq, const dnsdist_ffi_raw_value_t* values, size_t valuesCount)
@@ -658,9 +658,8 @@ void dnsdist_ffi_dnsquestion_spoof_raw(dnsdist_ffi_dnsquestion_t* dq, const dnsd
     data.emplace_back(values[idx].value, values[idx].size);
   }
 
-  std::string result;
-  SpoofAction tempSpoofAction(data, std::nullopt);
-  tempSpoofAction(dq->dq, &result);
+  dnsdist::ResponseConfig config{};
+  dnsdist::self_answers::generateAnswerFromRDataEntries(*dq->dq, data, std::nullopt, config);
 }
 
 void dnsdist_ffi_dnsquestion_spoof_addrs(dnsdist_ffi_dnsquestion_t* dq, const dnsdist_ffi_raw_value_t* values, size_t valuesCount)
@@ -687,9 +686,8 @@ void dnsdist_ffi_dnsquestion_spoof_addrs(dnsdist_ffi_dnsquestion_t* dq, const dn
     }
   }
 
-  std::string result;
-  SpoofAction tempSpoofAction(data);
-  tempSpoofAction(dq->dq, &result);
+  dnsdist::ResponseConfig config{};
+  dnsdist::self_answers::generateAnswerFromIPAddresses(*dq->dq, data, config);
 }
 
 void dnsdist_ffi_dnsquestion_set_max_returned_ttl(dnsdist_ffi_dnsquestion_t* dq, uint32_t max)
index 4fc06767c24202b47ef63e7be05a32cc78f81352..572b8a692eb90b82b434a670a57c5862a0a64eb7 100644 (file)
 extern RecursiveLockGuarded<LuaContext> g_lua;
 extern std::string g_outputBuffer; // locking for this is ok, as locked by g_luamutex
 
-class SpoofAction : public DNSAction
-{
-public:
-  SpoofAction(const vector<ComboAddress>& addrs) :
-    d_addrs(addrs)
-  {
-    for (const auto& addr : d_addrs) {
-      if (addr.isIPv4()) {
-        d_types.insert(QType::A);
-      }
-      else if (addr.isIPv6()) {
-        d_types.insert(QType::AAAA);
-      }
-    }
-
-    if (!d_addrs.empty()) {
-      d_types.insert(QType::ANY);
-    }
-  }
-
-  SpoofAction(const DNSName& cname) :
-    d_cname(cname)
-  {
-  }
-
-  SpoofAction(const char* rawresponse, size_t len) :
-    d_raw(rawresponse, rawresponse + len)
-  {
-  }
-
-  SpoofAction(const vector<std::string>& raws, std::optional<uint16_t> typeForAny) :
-    d_rawResponses(raws), d_rawTypeForAny(typeForAny)
-  {
-  }
-
-  DNSAction::Action operator()(DNSQuestion* dnsquestion, string* ruleresult) const override;
-
-  string toString() const override
-  {
-    string ret = "spoof in ";
-    if (!d_cname.empty()) {
-      ret += d_cname.toString() + " ";
-    }
-    if (d_rawResponses.size() > 0) {
-      ret += "raw bytes ";
-    }
-    else {
-      for (const auto& a : d_addrs)
-        ret += a.toString() + " ";
-    }
-    return ret;
-  }
-
-  [[nodiscard]] dnsdist::ResponseConfig& getResponseConfig()
-  {
-    return d_responseConfig;
-  }
-
-private:
-  dnsdist::ResponseConfig d_responseConfig;
-  static thread_local std::default_random_engine t_randomEngine;
-  std::vector<ComboAddress> d_addrs;
-  std::unordered_set<uint16_t> d_types;
-  std::vector<std::string> d_rawResponses;
-  PacketBuffer d_raw;
-  DNSName d_cname;
-  std::optional<uint16_t> d_rawTypeForAny{};
-};
-
 template <class T>
 using LuaArray = std::vector<std::pair<int, T>>;
 template <class T>
diff --git a/pdns/dnsdistdist/dnsdist-self-answers.cc b/pdns/dnsdistdist/dnsdist-self-answers.cc
new file mode 100644 (file)
index 0000000..dd0ff0b
--- /dev/null
@@ -0,0 +1,243 @@
+/*
+ * This file is part of PowerDNS or dnsdist.
+ * Copyright -- PowerDNS.COM B.V. and its contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In addition, for the avoidance of any doubt, permission is granted to
+ * link this program with OpenSSL and to (re)distribute the binaries
+ * produced as the result of such linking.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#include <random>
+
+#include "dnsdist-self-answers.hh"
+
+#include "dnsdist-configuration.hh"
+#include "dnsdist-ecs.hh"
+
+namespace dnsdist::self_answers
+{
+static thread_local std::default_random_engine t_randomEngine;
+
+static void addRecordHeader(PacketBuffer& packet, size_t& position, uint16_t qclass, uint32_t ttl, QType qtype, uint16_t rdataLen)
+{
+  std::array<unsigned char, 12> recordstart{
+    0xc0, 0x0c, // compressed name
+    0, 0, // QTYPE
+    0, 0, // QCLASS
+    0, 0, 0, 0, // TTL
+    0, 0 // rdata length
+  };
+  ttl = htonl(ttl);
+  qclass = htons(qclass);
+  qtype = htons(qtype);
+  rdataLen = htons(rdataLen);
+  static_assert(recordstart.size() == 12, "sizeof(recordstart) must be equal to 12, otherwise the above check is invalid");
+  memcpy(&recordstart.at(2), &qtype, sizeof(qtype));
+  memcpy(&recordstart.at(4), &qclass, sizeof(qclass));
+  memcpy(&recordstart.at(6), &ttl, sizeof(ttl));
+  memcpy(&recordstart.at(10), &rdataLen, sizeof(rdataLen));
+  memcpy(&packet.at(position), recordstart.data(), recordstart.size());
+  position += recordstart.size();
+}
+
+bool generateAnswerFromCNAME(DNSQuestion& dnsQuestion, const DNSName& cname, const dnsdist::ResponseConfig& responseConfig)
+{
+  QType qtype = QType::CNAME;
+  unsigned int totrdatalen = cname.getStorage().size();
+  size_t numberOfRecords = 1U;
+  auto qnameWireLength = dnsQuestion.ids.qname.wirelength();
+  if (dnsQuestion.getMaximumSize() < (sizeof(dnsheader) + qnameWireLength + 4 + numberOfRecords * 12 /* recordstart */ + totrdatalen)) {
+    return false;
+  }
+
+  bool dnssecOK = false;
+  bool hadEDNS = false;
+  if (dnsdist::configuration::getCurrentRuntimeConfiguration().d_addEDNSToSelfGeneratedResponses && queryHasEDNS(dnsQuestion)) {
+    hadEDNS = true;
+    dnssecOK = ((dnsdist::getEDNSZ(dnsQuestion) & EDNS_HEADER_FLAG_DO) != 0);
+  }
+
+  auto& data = dnsQuestion.getMutableData();
+  data.resize(sizeof(dnsheader) + qnameWireLength + 4 + numberOfRecords * 12 /* recordstart */ + totrdatalen); // there goes your EDNS
+  size_t position = sizeof(dnsheader) + qnameWireLength + 4;
+
+  dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsQuestion.getMutableData(), [responseConfig](dnsheader& header) {
+    header.qr = true; // for good measure
+    setResponseHeadersFromConfig(header, responseConfig);
+    header.ancount = 0;
+    header.arcount = 0; // for now, forget about your EDNS, we're marching over it
+    return true;
+  });
+
+  const auto& wireData = cname.getStorage(); // Note! This doesn't do compression!
+  addRecordHeader(data, position, dnsQuestion.ids.qclass, responseConfig.ttl, qtype, wireData.length());
+  memcpy(&data.at(position), wireData.c_str(), wireData.length());
+
+  dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsQuestion.getMutableData(), [numberOfRecords](dnsheader& header) {
+    header.ancount = htons(numberOfRecords);
+    return true;
+  });
+
+  if (hadEDNS) {
+    addEDNS(dnsQuestion.getMutableData(), dnsQuestion.getMaximumSize(), dnssecOK, dnsdist::configuration::getCurrentRuntimeConfiguration().d_payloadSizeSelfGenAnswers, 0);
+  }
+
+  return true;
+}
+
+bool generateAnswerFromIPAddresses(DNSQuestion& dnsQuestion, const std::vector<ComboAddress>& addresses, const dnsdist::ResponseConfig& responseConfig)
+{
+  uint16_t qtype = dnsQuestion.ids.qtype;
+  std::vector<ComboAddress> addrs = {};
+  unsigned int totrdatalen = 0;
+  size_t numberOfRecords = 0;
+  for (const auto& addr : addresses) {
+    if (qtype != QType::ANY && ((addr.sin4.sin_family == AF_INET && qtype != QType::A) || (addr.sin4.sin_family == AF_INET6 && qtype != QType::AAAA))) {
+      continue;
+    }
+    totrdatalen += addr.sin4.sin_family == AF_INET ? sizeof(addr.sin4.sin_addr.s_addr) : sizeof(addr.sin6.sin6_addr.s6_addr);
+    addrs.push_back(addr);
+    ++numberOfRecords;
+  }
+
+  if (addrs.size() > 1) {
+    shuffle(addrs.begin(), addrs.end(), t_randomEngine);
+  }
+
+  unsigned int qnameWireLength = dnsQuestion.ids.qname.wirelength();
+  if (dnsQuestion.getMaximumSize() < (sizeof(dnsheader) + qnameWireLength + 4 + numberOfRecords * 12 /* recordstart */ + totrdatalen)) {
+    return false;
+  }
+
+  bool dnssecOK = false;
+  bool hadEDNS = false;
+  if (dnsdist::configuration::getCurrentRuntimeConfiguration().d_addEDNSToSelfGeneratedResponses && queryHasEDNS(dnsQuestion)) {
+    hadEDNS = true;
+    dnssecOK = ((dnsdist::getEDNSZ(dnsQuestion) & EDNS_HEADER_FLAG_DO) != 0);
+  }
+
+  auto& data = dnsQuestion.getMutableData();
+  data.resize(sizeof(dnsheader) + qnameWireLength + 4 + numberOfRecords * 12 /* recordstart */ + totrdatalen); // there goes your EDNS
+  size_t position = sizeof(dnsheader) + qnameWireLength + 4;
+
+  dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsQuestion.getMutableData(), [responseConfig](dnsheader& header) {
+    header.qr = true; // for good measure
+    setResponseHeadersFromConfig(header, responseConfig);
+    header.ancount = 0;
+    header.arcount = 0; // for now, forget about your EDNS, we're marching over it
+    return true;
+  });
+
+  for (const auto& addr : addrs) {
+    uint16_t rdataLen = addr.sin4.sin_family == AF_INET ? sizeof(addr.sin4.sin_addr.s_addr) : sizeof(addr.sin6.sin6_addr.s6_addr);
+    qtype = addr.sin4.sin_family == AF_INET ? QType::A : QType::AAAA;
+
+    addRecordHeader(data, position, dnsQuestion.ids.qclass, responseConfig.ttl, qtype, rdataLen);
+
+    memcpy(&data.at(position),
+           // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+           addr.sin4.sin_family == AF_INET ? reinterpret_cast<const void*>(&addr.sin4.sin_addr.s_addr) : reinterpret_cast<const void*>(&addr.sin6.sin6_addr.s6_addr),
+           addr.sin4.sin_family == AF_INET ? sizeof(addr.sin4.sin_addr.s_addr) : sizeof(addr.sin6.sin6_addr.s6_addr));
+
+    position += (addr.sin4.sin_family == AF_INET ? sizeof(addr.sin4.sin_addr.s_addr) : sizeof(addr.sin6.sin6_addr.s6_addr));
+  }
+
+  auto finalANCount = addrs.size();
+  dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsQuestion.getMutableData(), [finalANCount](dnsheader& header) {
+    header.ancount = htons(finalANCount);
+    return true;
+  });
+
+  if (hadEDNS) {
+    addEDNS(dnsQuestion.getMutableData(), dnsQuestion.getMaximumSize(), dnssecOK, dnsdist::configuration::getCurrentRuntimeConfiguration().d_payloadSizeSelfGenAnswers, 0);
+  }
+
+  return true;
+}
+
+bool generateAnswerFromRDataEntries(DNSQuestion& dnsQuestion, const std::vector<std::string>& entries, std::optional<uint16_t> typeForAny, const dnsdist::ResponseConfig& responseConfig)
+{
+  unsigned int totrdatalen = 0;
+  size_t numberOfRecords = 0;
+  auto shuffledEntries = entries;
+  for (const auto& entry : shuffledEntries) {
+    totrdatalen += entry.size();
+    ++numberOfRecords;
+  }
+  if (shuffledEntries.size() > 1) {
+    shuffle(shuffledEntries.begin(), shuffledEntries.end(), t_randomEngine);
+  }
+
+  auto qnameWireLength = dnsQuestion.ids.qname.wirelength();
+  if (dnsQuestion.getMaximumSize() < (sizeof(dnsheader) + qnameWireLength + 4 + numberOfRecords * 12 /* recordstart */ + totrdatalen)) {
+    return false;
+  }
+
+  bool dnssecOK = false;
+  bool hadEDNS = false;
+  if (dnsdist::configuration::getCurrentRuntimeConfiguration().d_addEDNSToSelfGeneratedResponses && queryHasEDNS(dnsQuestion)) {
+    hadEDNS = true;
+    dnssecOK = ((dnsdist::getEDNSZ(dnsQuestion) & EDNS_HEADER_FLAG_DO) != 0);
+  }
+
+  auto& data = dnsQuestion.getMutableData();
+  data.resize(sizeof(dnsheader) + qnameWireLength + 4 + numberOfRecords * 12 /* recordstart */ + totrdatalen); // there goes your EDNS
+  size_t position = sizeof(dnsheader) + qnameWireLength + 4;
+
+  dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsQuestion.getMutableData(), [&responseConfig](dnsheader& header) {
+    header.qr = true; // for good measure
+    setResponseHeadersFromConfig(header, responseConfig);
+    header.ancount = 0;
+    header.arcount = 0; // for now, forget about your EDNS, we're marching over it
+    return true;
+  });
+
+  QType qtype = dnsQuestion.ids.qtype;
+  if (qtype == QType::ANY && typeForAny) {
+    qtype = *typeForAny;
+  }
+
+  for (const auto& entry : shuffledEntries) {
+    uint16_t rdataLen = entry.size();
+    addRecordHeader(data, position, dnsQuestion.ids.qclass, responseConfig.ttl, qtype, rdataLen);
+    memcpy(&data.at(position), entry.c_str(), entry.size());
+    position += entry.size();
+  }
+
+  auto finalANCount = shuffledEntries.size();
+  dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsQuestion.getMutableData(), [finalANCount](dnsheader& header) {
+    header.ancount = htons(finalANCount);
+    return true;
+  });
+
+  if (hadEDNS) {
+    addEDNS(dnsQuestion.getMutableData(), dnsQuestion.getMaximumSize(), dnssecOK, dnsdist::configuration::getCurrentRuntimeConfiguration().d_payloadSizeSelfGenAnswers, 0);
+  }
+
+  return true;
+}
+
+bool generateAnswerFromRawPacket(DNSQuestion& dnsQuestion, const PacketBuffer& packet)
+{
+  auto questionId = dnsQuestion.getHeader()->id;
+  dnsQuestion.getMutableData() = packet;
+  dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsQuestion.getMutableData(), [questionId](dnsheader& header) {
+    header.id = questionId;
+    return true;
+  });
+  return true;
+}
+
+}
diff --git a/pdns/dnsdistdist/dnsdist-self-answers.hh b/pdns/dnsdistdist/dnsdist-self-answers.hh
new file mode 100644 (file)
index 0000000..5703db4
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * This file is part of PowerDNS or dnsdist.
+ * Copyright -- PowerDNS.COM B.V. and its contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In addition, for the avoidance of any doubt, permission is granted to
+ * link this program with OpenSSL and to (re)distribute the binaries
+ * produced as the result of such linking.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "dnsdist.hh"
+#include "dnsdist-dnsparser.hh"
+
+namespace dnsdist::self_answers
+{
+bool generateAnswerFromCNAME(DNSQuestion& dnsQuestion, const DNSName& cname, const dnsdist::ResponseConfig& responseConfig);
+bool generateAnswerFromIPAddresses(DNSQuestion& dnsQuestion, const std::vector<ComboAddress>& addresses, const ResponseConfig& responseConfig);
+bool generateAnswerFromRDataEntries(DNSQuestion& dnsQuestion, const std::vector<std::string>& entries, std::optional<uint16_t> typeForAny, const ResponseConfig& responseConfig);
+bool generateAnswerFromRawPacket(DNSQuestion& dnsQuestion, const PacketBuffer& packet);
+}
index ee4f4d331620f4f9a7f760bbe87733f3fc4d3581..088f8fe46e1666603c2a37c0282d85090763ea8b 100644 (file)
@@ -58,6 +58,7 @@
 #include "dnsdist-random.hh"
 #include "dnsdist-rings.hh"
 #include "dnsdist-secpoll.hh"
+#include "dnsdist-self-answers.hh"
 #include "dnsdist-snmp.hh"
 #include "dnsdist-tcp.hh"
 #include "dnsdist-tcp-downstream.hh"
@@ -830,28 +831,28 @@ static void spoofResponseFromString(DNSQuestion& dnsQuestion, const string& spoo
   string result;
 
   if (raw) {
+    dnsdist::ResponseConfig config;
     std::vector<std::string> raws;
     stringtok(raws, spoofContent, ",");
-    SpoofAction tempSpoofAction(raws, std::nullopt);
-    tempSpoofAction(&dnsQuestion, &result);
+    dnsdist::self_answers::generateAnswerFromRDataEntries(dnsQuestion, raws, std::nullopt, config);
   }
   else {
     std::vector<std::string> addrs;
     stringtok(addrs, spoofContent, " ,");
 
     if (addrs.size() == 1) {
+      dnsdist::ResponseConfig config;
       try {
         ComboAddress spoofAddr(spoofContent);
-        SpoofAction tempSpoofAction({spoofAddr});
-        tempSpoofAction(&dnsQuestion, &result);
+        dnsdist::self_answers::generateAnswerFromIPAddresses(dnsQuestion, {spoofAddr}, config);
       }
       catch (const PDNSException& e) {
         DNSName cname(spoofContent);
-        SpoofAction tempSpoofAction(cname); // CNAME then
-        tempSpoofAction(&dnsQuestion, &result);
+        dnsdist::self_answers::generateAnswerFromCNAME(dnsQuestion, cname, config);
       }
     }
     else {
+      dnsdist::ResponseConfig config;
       std::vector<ComboAddress> cas;
       for (const auto& addr : addrs) {
         try {
@@ -860,18 +861,15 @@ static void spoofResponseFromString(DNSQuestion& dnsQuestion, const string& spoo
         catch (...) {
         }
       }
-      SpoofAction tempSpoofAction(cas);
-      tempSpoofAction(&dnsQuestion, &result);
+      dnsdist::self_answers::generateAnswerFromIPAddresses(dnsQuestion, cas, config);
     }
   }
 }
 
 static void spoofPacketFromString(DNSQuestion& dnsQuestion, const string& spoofContent)
 {
-  string result;
-
-  SpoofAction tempSpoofAction(spoofContent.c_str(), spoofContent.size());
-  tempSpoofAction(&dnsQuestion, &result);
+  // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
+  dnsdist::self_answers::generateAnswerFromRawPacket(dnsQuestion, PacketBuffer(spoofContent.data(), spoofContent.data() + spoofContent.size()));
 }
 
 bool processRulesResult(const DNSAction::Action& action, DNSQuestion& dnsQuestion, std::string& ruleresult, bool& drop)
index 3de6749da9f5445b2c1c6a3521a8f36f90e1c81c..014764974a3b22b34ed7ba7de40e00f32a588c26 100644 (file)
@@ -59,6 +59,7 @@ BOOST_AUTO_TEST_CASE(test_Query)
 
   DNSQuestion dq(ids, query);
   dnsdist_ffi_dnsquestion_t lightDQ(&dq);
+  const auto initialData = dq.getData();
 
   {
     // dnsdist_ffi_dnsquestion_get_qtype
@@ -260,14 +261,15 @@ BOOST_AUTO_TEST_CASE(test_Query)
   }
 
   {
-#if 0
-    // SpoofAction::operator() is a stub in the test runner
-    auto oldData = dq.getData();
+    dq.getMutableData() = initialData;
+    const auto oldData = dq.getData();
     std::vector<dnsdist_ffi_raw_value> values;
     ComboAddress v4("192.0.2.1");
     ComboAddress v6("[2001:db8::42]");
-    values.push_back({ reinterpret_cast<const char*>(&v4.sin4.sin_addr.s_addr), sizeof(v4.sin4.sin_addr.s_addr)});
-    values.push_back({ reinterpret_cast<const char*>(&v6.sin6.sin6_addr.s6_addr), sizeof(v6.sin6.sin6_addr.s6_addr)});
+    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+    values.push_back({reinterpret_cast<const char*>(&v4.sin4.sin_addr.s_addr), sizeof(v4.sin4.sin_addr.s_addr)});
+    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+    values.push_back({reinterpret_cast<const char*>(&v6.sin6.sin6_addr.s6_addr), sizeof(v6.sin6.sin6_addr.s6_addr)});
 
     dnsdist_ffi_dnsquestion_spoof_addrs(&lightDQ, values.data(), values.size());
     BOOST_CHECK(dq.getData().size() > oldData.size());
@@ -275,20 +277,17 @@ BOOST_AUTO_TEST_CASE(test_Query)
     MOADNSParser mdp(false, reinterpret_cast<const char*>(dq.getData().data()), dq.getData().size());
     BOOST_CHECK_EQUAL(mdp.d_qname, ids.qname);
     BOOST_CHECK_EQUAL(mdp.d_header.qdcount, 1U);
-    BOOST_CHECK_EQUAL(mdp.d_header.ancount, values.size());
+    /* only the A has been added since the query was not ANY */
+    BOOST_CHECK_EQUAL(mdp.d_header.ancount, 1U);
     BOOST_CHECK_EQUAL(mdp.d_header.nscount, 0U);
     BOOST_CHECK_EQUAL(mdp.d_header.arcount, 0U);
 
     BOOST_REQUIRE_EQUAL(mdp.d_answers.size(), 1U);
-    BOOST_CHECK_EQUAL(mdp.d_answers.at(0).first.d_type, static_cast<uint16_t>(QType::A));
-    BOOST_CHECK_EQUAL(mdp.d_answers.at(0).first.d_class, QClass::IN);
-    BOOST_CHECK_EQUAL(mdp.d_answers.at(0).first.d_name, ids.qname);
-    BOOST_CHECK_EQUAL(mdp.d_answers.at(1).first.d_type, static_cast<uint16_t>(QType::AAAA));
-    BOOST_CHECK_EQUAL(mdp.d_answers.at(1).first.d_class, QClass::IN);
-    BOOST_CHECK_EQUAL(mdp.d_answers.at(1).first.d_name, ids.qname);
+    BOOST_CHECK_EQUAL(mdp.d_answers.at(0).d_type, static_cast<uint16_t>(QType::A));
+    BOOST_CHECK_EQUAL(mdp.d_answers.at(0).d_class, QClass::IN);
+    BOOST_CHECK_EQUAL(mdp.d_answers.at(0).d_name, ids.qname);
 
     dq.getMutableData() = oldData;
-#endif
   }
 
   {
index e641dc63715475c6ae7d3d4fe34a17ce2a7da73a..9cdaa0b2a6c4ceaf0e9bd9d67ea5d9fcb1bfcab6 100644 (file)
@@ -42,11 +42,6 @@ void setLuaNoSideEffect()
 {
 }
 
-DNSAction::Action SpoofAction::operator()(DNSQuestion* dnsQuestion, std::string* ruleresult) const
-{
-  return DNSAction::Action::None;
-}
-
 bool setupDoTProtocolNegotiation(std::shared_ptr<TLSCtx>& tlsCtx)
 {
   (void)tlsCtx;