]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
Move Lua2BackendAPIv2 method bodies from .hh to .cc. NFC
authorMiod Vallat <miod.vallat@powerdns.com>
Mon, 13 Jul 2026 13:56:01 +0000 (15:56 +0200)
committerMiod Vallat <miod.vallat@powerdns.com>
Mon, 13 Jul 2026 14:17:24 +0000 (16:17 +0200)
Signed-off-by: Miod Vallat <miod.vallat@powerdns.com>
modules/lua2backend/lua2api2.cc
modules/lua2backend/lua2api2.hh

index d9a2304aba36504109bc1480a30ea3d74a16965b..5f2f5ecea765873ba95c96e3b7b6d92b784185b2 100644 (file)
 #endif
 #include "lua2backend.hh"
 
+Lua2BackendAPIv2::Lua2BackendAPIv2(Logr::log_t slog, const string& suffix)
+{
+  d_slog = slog;
+  d_include_path = ::arg()["lua-global-include-dir"];
+  setArgPrefix("lua2" + suffix);
+  d_debug_log = mustDo("query-logging");
+  prepareContext();
+  loadFile(getArg("filename"));
+}
+
 Lua2BackendAPIv2::~Lua2BackendAPIv2()
 {
   if (f_deinit)
     f_deinit();
 }
+
+void Lua2BackendAPIv2::postPrepareContext()
+{
+  AuthLua4::postPrepareContext();
+}
+
+void Lua2BackendAPIv2::postLoad()
+{
+  f_lookup = d_lw->readVariable<boost::optional<lookup_call_t>>("dns_lookup").get_value_or(0);
+  f_list = d_lw->readVariable<boost::optional<list_call_t>>("dns_list").get_value_or(0);
+  f_get_all_domains = d_lw->readVariable<boost::optional<get_all_domains_call_t>>("dns_get_all_domains").get_value_or(0);
+  f_get_domaininfo = d_lw->readVariable<boost::optional<get_domaininfo_call_t>>("dns_get_domaininfo").get_value_or(0);
+  f_get_domain_metadata = d_lw->readVariable<boost::optional<get_domain_metadata_call_t>>("dns_get_domain_metadata").get_value_or(0);
+  f_get_all_domain_metadata = d_lw->readVariable<boost::optional<get_all_domain_metadata_call_t>>("dns_get_all_domain_metadata").get_value_or(0);
+  f_get_domain_keys = d_lw->readVariable<boost::optional<get_domain_keys_call_t>>("dns_get_domain_keys").get_value_or(0);
+  f_get_before_and_after_names_absolute = d_lw->readVariable<boost::optional<get_before_and_after_names_absolute_call_t>>("dns_get_before_and_after_names_absolute").get_value_or(0);
+  f_set_notified = d_lw->readVariable<boost::optional<set_notified_call_t>>("dns_set_notified").get_value_or(0);
+
+  auto init = d_lw->readVariable<boost::optional<init_call_t>>("dns_init").get_value_or(0);
+  if (init) {
+    init();
+  }
+
+  f_deinit = d_lw->readVariable<boost::optional<deinit_call_t>>("dns_deinit").get_value_or(0);
+
+  if (f_lookup == nullptr) {
+    throw PDNSException("dns_lookup missing");
+  }
+
+  /* see if dnssec support is wanted */
+  d_dnssec = d_lw->readVariable<boost::optional<bool>>("dns_dnssec").get_value_or(false);
+  if (d_dnssec) {
+    if (f_get_domain_metadata == nullptr) {
+      throw PDNSException("dns_dnssec is true but dns_get_domain_metadata is missing");
+    }
+    if (f_get_before_and_after_names_absolute == nullptr) {
+      throw PDNSException("dns_dnssec is true but dns_get_before_and_after_names_absolute is missing");
+    }
+    /* domain keys is not strictly speaking necessary for dnssec backend */
+    if (f_get_domain_keys == nullptr) {
+      SLOG(g_log << Logger::Warning << "dns_get_domain_keys missing - cannot do live signing" << endl,
+           d_slog->info(Logr::Warning, "dns_get_domain_keys missing - cannot perform live signing"));
+    }
+  }
+}
+
+unsigned int Lua2BackendAPIv2::getCapabilities()
+{
+  unsigned int caps = CAP_DIRECT | CAP_LIST;
+  if (d_dnssec) {
+    caps |= CAP_DNSSEC;
+  }
+  if (f_get_all_domains != nullptr) {
+    caps |= CAP_SEARCH;
+  }
+  return caps;
+}
+
+void Lua2BackendAPIv2::parseLookup(const lookup_result_t& result)
+{
+  for (const auto& row : result) {
+    DNSResourceRecord rec;
+    for (const auto& item : row.second) {
+      if (item.first == "type") {
+        if (item.second.which() == 1) {
+          rec.qtype = QType(boost::get<int>(item.second));
+        }
+        else if (item.second.which() == 3) {
+          rec.qtype = boost::get<string>(item.second);
+        }
+        else if (item.second.which() == 4) {
+          rec.qtype = boost::get<QType>(item.second);
+        }
+        else {
+          throw PDNSException("Unsupported value for type");
+        }
+      }
+      else if (item.first == "name") {
+        if (item.second.which() == 3) {
+          rec.qname = DNSName(boost::get<string>(item.second));
+        }
+        else if (item.second.which() == 2) {
+          rec.qname = boost::get<DNSName>(item.second);
+        }
+        else {
+          throw PDNSException("Unsupported value for name");
+        }
+      }
+      else if (item.first == "domain_id") {
+        rec.domain_id = boost::get<int>(item.second);
+      }
+      else if (item.first == "auth") {
+        rec.auth = boost::get<bool>(item.second);
+      }
+      else if (item.first == "last_modified") {
+        rec.last_modified = static_cast<time_t>(boost::get<int>(item.second));
+      }
+      else if (item.first == "ttl") {
+        rec.ttl = boost::get<int>(item.second);
+      }
+      else if (item.first == "content") {
+        rec.setContent(boost::get<string>(item.second));
+      }
+      else if (item.first == "scopeMask") {
+        rec.scopeMask = boost::get<int>(item.second);
+      }
+      else {
+        SLOG(g_log << Logger::Warning << "Unsupported key '" << item.first << "' in lookup or list result" << endl,
+             d_slog->info(Logr::Warning, "Unsupported kep in lookup or list result", "key", Logging::Loggable(item.first)));
+      }
+    }
+    if (d_debug_log) {
+      SLOG(g_log << Logger::Debug << "[" << getPrefix() << "] Got result " << "'" << rec.qname << " IN " << rec.qtype.toString() << " " << rec.ttl << " " << rec.getZoneRepresentation() << "'" << endl,
+           d_slog->info(Logr::Debug, "Got result", "name", Logging::Loggable(rec.qname), "type", Logging::Loggable(rec.qtype), "ttl", Logging::Loggable(rec.ttl), "data", Logging::Loggable(rec.getZoneRepresentation())));
+    }
+    d_result.push_back(std::move(rec));
+  }
+  if (d_result.empty() && d_debug_log) {
+    SLOG(g_log << Logger::Debug << "[" << getPrefix() << "] Got empty result" << endl,
+         d_slog->info(Logr::Debug, "Got empty result"));
+  }
+}
+
+bool Lua2BackendAPIv2::list(const ZoneName& target, domainid_t domain_id, bool /* include_disabled */)
+{
+  if (f_list == nullptr) {
+    SLOG(g_log << Logger::Error << "[" << getPrefix() << "] dns_list missing - cannot do AXFR" << endl,
+         d_slog->info(Logr::Error, "dns_list missing - cannot perform AXFR"));
+    return false;
+  }
+
+  if (d_result.size() != 0) {
+    throw PDNSException("list attempted while another was running");
+  }
+
+  if (d_debug_log) {
+    SLOG(g_log << Logger::Debug << "[" << getPrefix() << "] Calling " << "list" << "(" << "target=" << target << ",domain_id=" << domain_id << ")" << endl,
+         d_slog->info(Logr::Debug, "Calling list", "target", Logging::Loggable(target), "domain id", Logging::Loggable(domain_id)));
+  }
+  list_result_t result = f_list(target.operator const DNSName&(), domain_id);
+
+  if (result.which() == 0) {
+    return false;
+  }
+
+  parseLookup(boost::get<lookup_result_t>(result));
+
+  return true;
+}
+
+void Lua2BackendAPIv2::lookup(const QType& qtype, const DNSName& qname, domainid_t domain_id, DNSPacket* p)
+{
+  if (d_result.size() != 0) {
+    throw PDNSException("lookup attempted while another was running");
+  }
+
+  lookup_context_t ctx;
+  if (p != NULL) {
+    ctx.emplace_back(lookup_context_t::value_type{"source_address", p->getInnerRemote().toString()});
+    ctx.emplace_back(lookup_context_t::value_type{"real_source_address", p->getRealRemote().toString()});
+  }
+
+  if (d_debug_log) {
+    SLOG(g_log << Logger::Debug << "[" << getPrefix() << "] Calling " << "lookup" << "(" << "qtype=" << qtype.toString() << ",qname=" << qname << ",domain_id=" << domain_id << ")" << endl,
+         d_slog->info(Logr::Debug, "Calling lookup", "type", Logging::Loggable(qtype), "name", Logging::Loggable(qname), "domain id", Logging::Loggable(domain_id)));
+  }
+  lookup_result_t result = f_lookup(qtype, qname, domain_id, ctx);
+  parseLookup(result);
+}
+
+bool Lua2BackendAPIv2::get(DNSResourceRecord& rr)
+{
+  if (d_result.size() == 0) {
+    return false;
+  }
+  rr = std::move(d_result.front());
+  d_result.pop_front();
+  return true;
+}
+
+string Lua2BackendAPIv2::directBackendCmd(const string& querystr)
+{
+  string::size_type pos = querystr.find_first_of(" \t");
+  string cmd = querystr;
+  string par = "";
+  if (pos != string::npos) {
+    cmd = querystr.substr(0, pos);
+    par = querystr.substr(pos + 1);
+  }
+  direct_backend_cmd_call_t f = d_lw->readVariable<boost::optional<direct_backend_cmd_call_t>>(cmd).get_value_or(0);
+  if (f == nullptr) {
+    return cmd + "not found";
+  }
+  if (d_debug_log) {
+    SLOG(g_log << Logger::Debug << "[" << getPrefix() << "] Calling " << cmd << "(" << "parameter=" << par << ")" << endl,
+         d_slog->info(Logr::Debug, "Direct backend command", "command", Logging::Loggable(cmd), "parameters", Logging::Loggable(par)));
+  }
+  return f(par);
+}
+
+void Lua2BackendAPIv2::setNotified(domainid_t id, uint32_t serial)
+{
+  if (f_set_notified == NULL) {
+    return;
+  }
+  if (d_debug_log) {
+    SLOG(g_log << Logger::Debug << "[" << getPrefix() << "] Calling " << "dns_set_notified" << "(" << "id=" << id << ",serial=" << serial << ")" << endl,
+         d_slog->info(Logr::Debug, "Calling dns_set_notified", "id", Logging::Loggable(id), "serial", Logging::Loggable(serial)));
+  }
+  f_set_notified(id, serial);
+}
+
+void Lua2BackendAPIv2::parseDomainInfo(const domaininfo_result_t& row, DomainInfo& di)
+{
+  di.id = UnknownDomainID;
+  for (const auto& item : row) {
+    if (item.first == "account") {
+      di.account = boost::get<string>(item.second);
+    }
+    else if (item.first == "last_check") {
+      di.last_check = static_cast<time_t>(boost::get<long>(item.second));
+    }
+    else if (item.first == "masters") {
+      for (const auto& primary : boost::get<vector<string>>(item.second)) {
+        di.primaries.push_back(ComboAddress(primary, 53));
+      }
+    }
+    else if (item.first == "id") {
+      di.id = static_cast<domainid_t>(boost::get<long>(item.second));
+    }
+    else if (item.first == "notified_serial") {
+      di.notified_serial = static_cast<unsigned int>(boost::get<long>(item.second));
+    }
+    else if (item.first == "serial") {
+      di.serial = static_cast<unsigned int>(boost::get<long>(item.second));
+    }
+    else if (item.first == "kind") {
+      di.kind = DomainInfo::stringToKind(boost::get<string>(item.second));
+    }
+    else {
+      SLOG(g_log << Logger::Warning << "Unsupported key '" << item.first << "' in domaininfo result" << endl,
+           d_slog->info(Logr::Warning, "Unsupported key in domaininfo result", "key", Logging::Loggable(item.first)));
+    }
+  }
+  di.backend = this;
+  if (d_debug_log) {
+    SLOG(g_log << Logger::Debug << "[" << getPrefix() << "] Got result " << "'" << "zone=" << di.zone << ",serial=" << di.serial << ",kind=" << di.getKindString() << "'" << endl,
+         d_slog->info(Logr::Debug, "Got domain info", "zone", Logging::Loggable(di.zone), "serial", Logging::Loggable(di.serial), "kind", Logging::Loggable(di.getKindString())));
+  }
+}
+
+bool Lua2BackendAPIv2::getDomainInfo(const ZoneName& domain, DomainInfo& di, bool /* getSerial */)
+{
+  if (f_get_domaininfo == nullptr) {
+    // use getAuth instead... but getAuth wraps getSOA which will call
+    // getDomainInfo if this is a domain variant, so protect against this
+    // would-be infinite recursion.
+    if (domain.hasVariant()) {
+      SLOG(g_log << Logger::Info << "Unable to return domain information for '" << domain.toLogString() << "' due to unimplemented dns_get_domaininfo" << endl,
+           d_slog->info(Logr::Info, "Unable to return domain information due to unimplemented dns_get_domaininfo", "domain", Logging::Loggable(domain)));
+      return false;
+    }
+    SOAData sd;
+    if (!getAuth(domain, &sd)) {
+      return false;
+    }
+
+    di.id = sd.domain_id;
+    di.zone = domain;
+    di.backend = this;
+    di.serial = sd.serial;
+    return true;
+  }
+
+  if (d_debug_log) {
+    SLOG(g_log << Logger::Debug << "[" << getPrefix() << "] Calling " << "get_domaininfo" << "(" << "domain=" << domain << ")" << endl,
+         d_slog->info(Logr::Debug, "Calling get_domaininfo", "domain", Logging::Loggable(domain)));
+  }
+  get_domaininfo_result_t result = f_get_domaininfo(domain.operator const DNSName&());
+
+  if (result.which() == 0) {
+    return false;
+  }
+
+  di.zone = domain;
+  parseDomainInfo(boost::get<domaininfo_result_t>(result), di);
+
+  return true;
+}
+
+void Lua2BackendAPIv2::getAllDomains(vector<DomainInfo>* domains, bool /* getSerial */, bool /* include_disabled */)
+{
+  if (f_get_all_domains == nullptr) {
+    return;
+  }
+
+  if (d_debug_log) {
+    SLOG(g_log << Logger::Debug << "[" << getPrefix() << "] Calling " << "get_all_domains" << "(" << "" << ")" << endl,
+         d_slog->info(Logr::Debug, "Calling get_all_domains"));
+  }
+  for (const auto& row : f_get_all_domains()) {
+    DomainInfo di;
+    di.zone = ZoneName(row.first);
+    if (d_debug_log) {
+      SLOG(g_log << Logger::Debug << "[" << getPrefix() << "] Got result " << "'" << di.zone << "'" << endl,
+           d_slog->info(Logr::Debug, "Got result", "domain", Logging::Loggable(di.zone)));
+    }
+    parseDomainInfo(row.second, di);
+    domains->push_back(std::move(di));
+  }
+}
+
+bool Lua2BackendAPIv2::getAllDomainMetadata(const ZoneName& name, std::map<std::string, std::vector<std::string>>& meta)
+{
+  if (f_get_all_domain_metadata == nullptr) {
+    return false;
+  }
+
+  if (d_debug_log) {
+    SLOG(g_log << Logger::Debug << "[" << getPrefix() << "] Calling " << "get_all_domain_metadata" << "(" << "name=" << name << ")" << endl,
+         d_slog->info(Logr::Debug, "Calling get_all_domain_metadata", "domain", Logging::Loggable(name)));
+  }
+  get_all_domain_metadata_result_t result = f_get_all_domain_metadata(name.operator const DNSName&());
+  if (result.which() == 0) {
+    return false;
+  }
+
+  for (const auto& row : boost::get<vector<pair<string, domain_metadata_result_t>>>(result)) {
+    meta[row.first].clear();
+    for (const auto& item : row.second) {
+      meta[row.first].push_back(item.second);
+    }
+    if (d_debug_log) {
+      SLOG(g_log << Logger::Debug << "[" << getPrefix() << "] Got result " << "'" << "kind=" << row.first << ",value=" << boost::algorithm::join(meta[row.first], ", ") << "'" << endl,
+           d_slog->info(Logr::Debug, "Got result", "kind", Logging::Loggable(row.first), "value", Logging::Loggable(boost::algorithm::join(meta[row.first], ", "))));
+    }
+  }
+
+  return true;
+}
+
+bool Lua2BackendAPIv2::getDomainMetadata(const ZoneName& name, const std::string& kind, std::vector<std::string>& meta)
+{
+  if (f_get_domain_metadata == nullptr) {
+    return false;
+  }
+
+  if (d_debug_log) {
+    SLOG(g_log << Logger::Debug << "[" << getPrefix() << "] Calling " << "get_domain_metadata" << "(" << "name=" << name << ",kind=" << kind << ")" << endl,
+         d_slog->info(Logr::Debug, "Calling get_domain_metadata", "domain", Logging::Loggable(name), "kind", Logging::Loggable(kind)));
+  }
+  get_domain_metadata_result_t result = f_get_domain_metadata(name.operator const DNSName&(), kind);
+  if (result.which() == 0) {
+    return false;
+  }
+
+  meta.clear();
+  for (const auto& item : boost::get<domain_metadata_result_t>(result)) {
+    meta.push_back(item.second);
+  }
+
+  if (d_debug_log) {
+    SLOG(g_log << Logger::Debug << "[" << getPrefix() << "] Got result " << "'" << "value=" << boost::algorithm::join(meta, ", ") << "'" << endl,
+         d_slog->info(Logr::Debug, "Got result", "value", Logging::Loggable(boost::algorithm::join(meta, ", "))));
+  }
+  return true;
+}
+
+bool Lua2BackendAPIv2::getDomainKeys(const ZoneName& name, std::vector<DNSBackend::KeyData>& keys)
+{
+  if (f_get_domain_keys == nullptr) {
+    return false;
+  }
+
+  if (d_debug_log) {
+    SLOG(g_log << Logger::Debug << "[" << getPrefix() << "] Calling " << "get_domain_keys" << "(" << "name=" << name << ")" << endl,
+         d_slog->info(Logr::Debug, "Calling get_domain_keys", "domain", Logging::Loggable(name)));
+  }
+  get_domain_keys_result_t result = f_get_domain_keys(name.operator const DNSName&());
+
+  if (result.which() == 0) {
+    return false;
+  }
+
+  for (const auto& row : boost::get<vector<pair<int, keydata_result_t>>>(result)) {
+    DNSBackend::KeyData key;
+    key.published = true;
+    for (const auto& item : row.second) {
+      if (item.first == "content") {
+        key.content = boost::get<string>(item.second);
+      }
+      else if (item.first == "id") {
+        key.id = static_cast<unsigned int>(boost::get<int>(item.second));
+      }
+      else if (item.first == "flags") {
+        key.flags = static_cast<unsigned int>(boost::get<int>(item.second));
+      }
+      else if (item.first == "active") {
+        key.active = boost::get<bool>(item.second);
+      }
+      else if (item.first == "published") {
+        key.published = boost::get<bool>(item.second);
+      }
+      else {
+        SLOG(g_log << Logger::Warning << "[" << getPrefix() << "] Unsupported key '" << item.first << "' in keydata result" << endl,
+             d_slog->info(Logr::Warning, "Unsupported key in keydata result", "key", Logging::Loggable(item.first)));
+      }
+    }
+    if (d_debug_log) {
+      SLOG(g_log << Logger::Debug << "[" << getPrefix() << "] Got result " << "'" << "id=" << key.id << ",flags=" << key.flags << ",active=" << (key.active ? "true" : "false") << ",published=" << (key.published ? "true" : "false") << "'" << endl,
+           d_slog->info(Logr::Debug, "Got result", "id", Logging::Loggable(key.id), "flags", Logging::Loggable(key.flags), "active", Logging::Loggable(key.active ? "true" : "false"), "published", Logging::Loggable(key.published ? "true" : "false")));
+    }
+    keys.emplace_back(std::move(key));
+  }
+
+  return true;
+}
+
+bool Lua2BackendAPIv2::getBeforeAndAfterNamesAbsolute(domainid_t id, const DNSName& qname, DNSName& unhashed, DNSName& before, DNSName& after)
+{
+  if (f_get_before_and_after_names_absolute == nullptr) {
+    return false;
+  }
+
+  if (d_debug_log) {
+    SLOG(g_log << Logger::Debug << "[" << getPrefix() << "] Calling " << "get_before_and_after_names_absolute" << "(" << "id=<<" << id << ",qname=" << qname << ")" << endl,
+         d_slog->info(Logr::Debug, "Calling get_before_and_after_names_absolute", "id", Logging::Loggable(id), "name", Logging::Loggable(qname)));
+  }
+  get_before_and_after_names_absolute_result_t result = f_get_before_and_after_names_absolute(id, qname);
+
+  if (result.which() == 0) {
+    return false;
+  }
+
+  before_and_after_names_result_t row = boost::get<before_and_after_names_result_t>(result);
+  if (row.size() != 3) {
+    SLOG(g_log << Logger::Error << "Invalid result from dns_get_before_and_after_names_absolute, expected array with 3 items, got " << row.size() << "item(s)" << endl,
+         d_slog->info(Logr::Error, "Invalid result from dns_get_before_and_after_names_absolute, expected array with 3 rows", "rows returned", Logging::Loggable(row.size())));
+    return false;
+  }
+  for (const auto& item : row) {
+    DNSName value;
+    if (item.second.which() == 0) {
+      value = DNSName(boost::get<string>(item.second));
+    }
+    else {
+      value = DNSName(boost::get<DNSName>(item.second));
+    }
+    if (item.first == "unhashed") {
+      unhashed = std::move(value);
+    }
+    else if (item.first == "before") {
+      before = std::move(value);
+    }
+    else if (item.first == "after") {
+      after = std::move(value);
+    }
+    else {
+      SLOG(g_log << Logger::Error << "Invalid result from dns_get_before_and_after_names_absolute, unexpected key " << item.first << endl,
+           d_slog->info(Logr::Error, "Invalid result from dns_get_before_and_after_names_absolute, unexpected key", "key", Logging::Loggable(item.first)));
+      return false;
+    }
+  }
+
+  if (d_debug_log) {
+    SLOG(g_log << Logger::Debug << "[" << getPrefix() << "] Got result " << "'" << "unhashed=" << unhashed << ",before=" << before << ",after=" << after << "'" << endl,
+         d_slog->info(Logr::Debug, "Got result", "unhashed", Logging::Loggable(unhashed), "before", Logging::Loggable(before), "after", Logging::Loggable(after)));
+  }
+  return true;
+}
index a301b8264a434849b310e806ff6fcd03ee89f9bb..e1981105d9deada95c09b1871c6999d3cbdcc8d2 100644 (file)
@@ -65,487 +65,24 @@ private:
   typedef std::function<string(const string& cmd)> direct_backend_cmd_call_t;
 
 public:
-  Lua2BackendAPIv2(Logr::log_t slog, const string& suffix)
-  {
-    d_slog = slog;
-    d_include_path = ::arg()["lua-global-include-dir"];
-    setArgPrefix("lua2" + suffix);
-    d_debug_log = mustDo("query-logging");
-    prepareContext();
-    loadFile(getArg("filename"));
-  }
-
+  Lua2BackendAPIv2(Logr::log_t slog, const string& suffix);
   ~Lua2BackendAPIv2() override;
 
-  void postPrepareContext() override
-  {
-    AuthLua4::postPrepareContext();
-  }
-
-  void postLoad() override
-  {
-    f_lookup = d_lw->readVariable<boost::optional<lookup_call_t>>("dns_lookup").get_value_or(0);
-    f_list = d_lw->readVariable<boost::optional<list_call_t>>("dns_list").get_value_or(0);
-    f_get_all_domains = d_lw->readVariable<boost::optional<get_all_domains_call_t>>("dns_get_all_domains").get_value_or(0);
-    f_get_domaininfo = d_lw->readVariable<boost::optional<get_domaininfo_call_t>>("dns_get_domaininfo").get_value_or(0);
-    f_get_domain_metadata = d_lw->readVariable<boost::optional<get_domain_metadata_call_t>>("dns_get_domain_metadata").get_value_or(0);
-    f_get_all_domain_metadata = d_lw->readVariable<boost::optional<get_all_domain_metadata_call_t>>("dns_get_all_domain_metadata").get_value_or(0);
-    f_get_domain_keys = d_lw->readVariable<boost::optional<get_domain_keys_call_t>>("dns_get_domain_keys").get_value_or(0);
-    f_get_before_and_after_names_absolute = d_lw->readVariable<boost::optional<get_before_and_after_names_absolute_call_t>>("dns_get_before_and_after_names_absolute").get_value_or(0);
-    f_set_notified = d_lw->readVariable<boost::optional<set_notified_call_t>>("dns_set_notified").get_value_or(0);
-
-    auto init = d_lw->readVariable<boost::optional<init_call_t>>("dns_init").get_value_or(0);
-    if (init) {
-      init();
-    }
-
-    f_deinit = d_lw->readVariable<boost::optional<deinit_call_t>>("dns_deinit").get_value_or(0);
-
-    if (f_lookup == nullptr) {
-      throw PDNSException("dns_lookup missing");
-    }
-
-    /* see if dnssec support is wanted */
-    d_dnssec = d_lw->readVariable<boost::optional<bool>>("dns_dnssec").get_value_or(false);
-    if (d_dnssec) {
-      if (f_get_domain_metadata == nullptr) {
-        throw PDNSException("dns_dnssec is true but dns_get_domain_metadata is missing");
-      }
-      if (f_get_before_and_after_names_absolute == nullptr) {
-        throw PDNSException("dns_dnssec is true but dns_get_before_and_after_names_absolute is missing");
-      }
-      /* domain keys is not strictly speaking necessary for dnssec backend */
-      if (f_get_domain_keys == nullptr) {
-        SLOG(g_log << Logger::Warning << "dns_get_domain_keys missing - cannot do live signing" << endl,
-             d_slog->info(Logr::Warning, "dns_get_domain_keys missing - cannot perform live signing"));
-      }
-    }
-  }
-
-  unsigned int getCapabilities() override
-  {
-    unsigned int caps = CAP_DIRECT | CAP_LIST;
-    if (d_dnssec) {
-      caps |= CAP_DNSSEC;
-    }
-    if (f_get_all_domains != nullptr) {
-      caps |= CAP_SEARCH;
-    }
-    return caps;
-  }
-
-  void parseLookup(const lookup_result_t& result)
-  {
-    for (const auto& row : result) {
-      DNSResourceRecord rec;
-      for (const auto& item : row.second) {
-        if (item.first == "type") {
-          if (item.second.which() == 1) {
-            rec.qtype = QType(boost::get<int>(item.second));
-          }
-          else if (item.second.which() == 3) {
-            rec.qtype = boost::get<string>(item.second);
-          }
-          else if (item.second.which() == 4) {
-            rec.qtype = boost::get<QType>(item.second);
-          }
-          else {
-            throw PDNSException("Unsupported value for type");
-          }
-        }
-        else if (item.first == "name") {
-          if (item.second.which() == 3) {
-            rec.qname = DNSName(boost::get<string>(item.second));
-          }
-          else if (item.second.which() == 2) {
-            rec.qname = boost::get<DNSName>(item.second);
-          }
-          else {
-            throw PDNSException("Unsupported value for name");
-          }
-        }
-        else if (item.first == "domain_id") {
-          rec.domain_id = boost::get<int>(item.second);
-        }
-        else if (item.first == "auth") {
-          rec.auth = boost::get<bool>(item.second);
-        }
-        else if (item.first == "last_modified") {
-          rec.last_modified = static_cast<time_t>(boost::get<int>(item.second));
-        }
-        else if (item.first == "ttl") {
-          rec.ttl = boost::get<int>(item.second);
-        }
-        else if (item.first == "content") {
-          rec.setContent(boost::get<string>(item.second));
-        }
-        else if (item.first == "scopeMask") {
-          rec.scopeMask = boost::get<int>(item.second);
-        }
-        else {
-          SLOG(g_log << Logger::Warning << "Unsupported key '" << item.first << "' in lookup or list result" << endl,
-               d_slog->info(Logr::Warning, "Unsupported kep in lookup or list result", "key", Logging::Loggable(item.first)));
-        }
-      }
-      if (d_debug_log) {
-        SLOG(g_log << Logger::Debug << "[" << getPrefix() << "] Got result " << "'" << rec.qname << " IN " << rec.qtype.toString() << " " << rec.ttl << " " << rec.getZoneRepresentation() << "'" << endl,
-             d_slog->info(Logr::Debug, "Got result", "name", Logging::Loggable(rec.qname), "type", Logging::Loggable(rec.qtype), "ttl", Logging::Loggable(rec.ttl), "data", Logging::Loggable(rec.getZoneRepresentation())));
-      }
-      d_result.push_back(std::move(rec));
-    }
-    if (d_result.empty() && d_debug_log) {
-      SLOG(g_log << Logger::Debug << "[" << getPrefix() << "] Got empty result" << endl,
-           d_slog->info(Logr::Debug, "Got empty result"));
-    }
-  }
-
-  bool list(const ZoneName& target, domainid_t domain_id, bool /* include_disabled */ = false) override
-  {
-    if (f_list == nullptr) {
-      SLOG(g_log << Logger::Error << "[" << getPrefix() << "] dns_list missing - cannot do AXFR" << endl,
-           d_slog->info(Logr::Error, "dns_list missing - cannot perform AXFR"));
-      return false;
-    }
-
-    if (d_result.size() != 0) {
-      throw PDNSException("list attempted while another was running");
-    }
-
-    if (d_debug_log) {
-      SLOG(g_log << Logger::Debug << "[" << getPrefix() << "] Calling " << "list" << "(" << "target=" << target << ",domain_id=" << domain_id << ")" << endl,
-           d_slog->info(Logr::Debug, "Calling list", "target", Logging::Loggable(target), "domain id", Logging::Loggable(domain_id)));
-    }
-    list_result_t result = f_list(target.operator const DNSName&(), domain_id);
-
-    if (result.which() == 0) {
-      return false;
-    }
-
-    parseLookup(boost::get<lookup_result_t>(result));
-
-    return true;
-  }
-
-  void lookup(const QType& qtype, const DNSName& qname, domainid_t domain_id, DNSPacket* p = nullptr) override
-  {
-    if (d_result.size() != 0) {
-      throw PDNSException("lookup attempted while another was running");
-    }
-
-    lookup_context_t ctx;
-    if (p != NULL) {
-      ctx.emplace_back(lookup_context_t::value_type{"source_address", p->getInnerRemote().toString()});
-      ctx.emplace_back(lookup_context_t::value_type{"real_source_address", p->getRealRemote().toString()});
-    }
-
-    if (d_debug_log) {
-      SLOG(g_log << Logger::Debug << "[" << getPrefix() << "] Calling " << "lookup" << "(" << "qtype=" << qtype.toString() << ",qname=" << qname << ",domain_id=" << domain_id << ")" << endl,
-           d_slog->info(Logr::Debug, "Calling lookup", "type", Logging::Loggable(qtype), "name", Logging::Loggable(qname), "domain id", Logging::Loggable(domain_id)));
-    }
-    lookup_result_t result = f_lookup(qtype, qname, domain_id, ctx);
-    parseLookup(result);
-  }
-
-  bool get(DNSResourceRecord& rr) override
-  {
-    if (d_result.size() == 0) {
-      return false;
-    }
-    rr = std::move(d_result.front());
-    d_result.pop_front();
-    return true;
-  }
-
-  string directBackendCmd(const string& querystr) override
-  {
-    string::size_type pos = querystr.find_first_of(" \t");
-    string cmd = querystr;
-    string par = "";
-    if (pos != string::npos) {
-      cmd = querystr.substr(0, pos);
-      par = querystr.substr(pos + 1);
-    }
-    direct_backend_cmd_call_t f = d_lw->readVariable<boost::optional<direct_backend_cmd_call_t>>(cmd).get_value_or(0);
-    if (f == nullptr) {
-      return cmd + "not found";
-    }
-    if (d_debug_log) {
-      SLOG(g_log << Logger::Debug << "[" << getPrefix() << "] Calling " << cmd << "(" << "parameter=" << par << ")" << endl,
-           d_slog->info(Logr::Debug, "Direct backend command", "command", Logging::Loggable(cmd), "parameters", Logging::Loggable(par)));
-    }
-    return f(par);
-  }
-
-  void setNotified(domainid_t id, uint32_t serial) override
-  {
-    if (f_set_notified == NULL) {
-      return;
-    }
-    if (d_debug_log) {
-      SLOG(g_log << Logger::Debug << "[" << getPrefix() << "] Calling " << "dns_set_notified" << "(" << "id=" << id << ",serial=" << serial << ")" << endl,
-           d_slog->info(Logr::Debug, "Calling dns_set_notified", "id", Logging::Loggable(id), "serial", Logging::Loggable(serial)));
-    }
-    f_set_notified(id, serial);
-  }
-
-  void parseDomainInfo(const domaininfo_result_t& row, DomainInfo& di)
-  {
-    di.id = UnknownDomainID;
-    for (const auto& item : row) {
-      if (item.first == "account") {
-        di.account = boost::get<string>(item.second);
-      }
-      else if (item.first == "last_check") {
-        di.last_check = static_cast<time_t>(boost::get<long>(item.second));
-      }
-      else if (item.first == "masters") {
-        for (const auto& primary : boost::get<vector<string>>(item.second)) {
-          di.primaries.push_back(ComboAddress(primary, 53));
-        }
-      }
-      else if (item.first == "id") {
-        di.id = static_cast<domainid_t>(boost::get<long>(item.second));
-      }
-      else if (item.first == "notified_serial") {
-        di.notified_serial = static_cast<unsigned int>(boost::get<long>(item.second));
-      }
-      else if (item.first == "serial") {
-        di.serial = static_cast<unsigned int>(boost::get<long>(item.second));
-      }
-      else if (item.first == "kind") {
-        di.kind = DomainInfo::stringToKind(boost::get<string>(item.second));
-      }
-      else {
-        SLOG(g_log << Logger::Warning << "Unsupported key '" << item.first << "' in domaininfo result" << endl,
-             d_slog->info(Logr::Warning, "Unsupported key in domaininfo result", "key", Logging::Loggable(item.first)));
-      }
-    }
-    di.backend = this;
-    if (d_debug_log) {
-      SLOG(g_log << Logger::Debug << "[" << getPrefix() << "] Got result " << "'" << "zone=" << di.zone << ",serial=" << di.serial << ",kind=" << di.getKindString() << "'" << endl,
-           d_slog->info(Logr::Debug, "Got domain info", "zone", Logging::Loggable(di.zone), "serial", Logging::Loggable(di.serial), "kind", Logging::Loggable(di.getKindString())));
-    }
-  }
-
-  bool getDomainInfo(const ZoneName& domain, DomainInfo& di, bool /* getSerial */ = true) override
-  {
-    if (f_get_domaininfo == nullptr) {
-      // use getAuth instead... but getAuth wraps getSOA which will call
-      // getDomainInfo if this is a domain variant, so protect against this
-      // would-be infinite recursion.
-      if (domain.hasVariant()) {
-        SLOG(g_log << Logger::Info << "Unable to return domain information for '" << domain.toLogString() << "' due to unimplemented dns_get_domaininfo" << endl,
-             d_slog->info(Logr::Info, "Unable to return domain information due to unimplemented dns_get_domaininfo", "domain", Logging::Loggable(domain)));
-        return false;
-      }
-      SOAData sd;
-      if (!getAuth(domain, &sd)) {
-        return false;
-      }
-
-      di.id = sd.domain_id;
-      di.zone = domain;
-      di.backend = this;
-      di.serial = sd.serial;
-      return true;
-    }
-
-    if (d_debug_log) {
-      SLOG(g_log << Logger::Debug << "[" << getPrefix() << "] Calling " << "get_domaininfo" << "(" << "domain=" << domain << ")" << endl,
-           d_slog->info(Logr::Debug, "Calling get_domaininfo", "domain", Logging::Loggable(domain)));
-    }
-    get_domaininfo_result_t result = f_get_domaininfo(domain.operator const DNSName&());
-
-    if (result.which() == 0) {
-      return false;
-    }
-
-    di.zone = domain;
-    parseDomainInfo(boost::get<domaininfo_result_t>(result), di);
-
-    return true;
-  }
-
-  void getAllDomains(vector<DomainInfo>* domains, bool /* getSerial */, bool /* include_disabled */) override
-  {
-    if (f_get_all_domains == nullptr) {
-      return;
-    }
-
-    if (d_debug_log) {
-      SLOG(g_log << Logger::Debug << "[" << getPrefix() << "] Calling " << "get_all_domains" << "(" << "" << ")" << endl,
-           d_slog->info(Logr::Debug, "Calling get_all_domains"));
-    }
-    for (const auto& row : f_get_all_domains()) {
-      DomainInfo di;
-      di.zone = ZoneName(row.first);
-      if (d_debug_log) {
-        SLOG(g_log << Logger::Debug << "[" << getPrefix() << "] Got result " << "'" << di.zone << "'" << endl,
-             d_slog->info(Logr::Debug, "Got result", "domain", Logging::Loggable(di.zone)));
-      }
-      parseDomainInfo(row.second, di);
-      domains->push_back(std::move(di));
-    }
-  }
-
-  bool getAllDomainMetadata(const ZoneName& name, std::map<std::string, std::vector<std::string>>& meta) override
-  {
-    if (f_get_all_domain_metadata == nullptr) {
-      return false;
-    }
-
-    if (d_debug_log) {
-      SLOG(g_log << Logger::Debug << "[" << getPrefix() << "] Calling " << "get_all_domain_metadata" << "(" << "name=" << name << ")" << endl,
-           d_slog->info(Logr::Debug, "Calling get_all_domain_metadata", "domain", Logging::Loggable(name)));
-    }
-    get_all_domain_metadata_result_t result = f_get_all_domain_metadata(name.operator const DNSName&());
-    if (result.which() == 0) {
-      return false;
-    }
-
-    for (const auto& row : boost::get<vector<pair<string, domain_metadata_result_t>>>(result)) {
-      meta[row.first].clear();
-      for (const auto& item : row.second) {
-        meta[row.first].push_back(item.second);
-      }
-      if (d_debug_log) {
-        SLOG(g_log << Logger::Debug << "[" << getPrefix() << "] Got result " << "'" << "kind=" << row.first << ",value=" << boost::algorithm::join(meta[row.first], ", ") << "'" << endl,
-             d_slog->info(Logr::Debug, "Got result", "kind", Logging::Loggable(row.first), "value", Logging::Loggable(boost::algorithm::join(meta[row.first], ", "))));
-      }
-    }
-
-    return true;
-  }
-
-  bool getDomainMetadata(const ZoneName& name, const std::string& kind, std::vector<std::string>& meta) override
-  {
-    if (f_get_domain_metadata == nullptr) {
-      return false;
-    }
-
-    if (d_debug_log) {
-      SLOG(g_log << Logger::Debug << "[" << getPrefix() << "] Calling " << "get_domain_metadata" << "(" << "name=" << name << ",kind=" << kind << ")" << endl,
-           d_slog->info(Logr::Debug, "Calling get_domain_metadata", "domain", Logging::Loggable(name), "kind", Logging::Loggable(kind)));
-    }
-    get_domain_metadata_result_t result = f_get_domain_metadata(name.operator const DNSName&(), kind);
-    if (result.which() == 0) {
-      return false;
-    }
-
-    meta.clear();
-    for (const auto& item : boost::get<domain_metadata_result_t>(result)) {
-      meta.push_back(item.second);
-    }
-
-    if (d_debug_log) {
-      SLOG(g_log << Logger::Debug << "[" << getPrefix() << "] Got result " << "'" << "value=" << boost::algorithm::join(meta, ", ") << "'" << endl,
-           d_slog->info(Logr::Debug, "Got result", "value", Logging::Loggable(boost::algorithm::join(meta, ", "))));
-    }
-    return true;
-  }
-
-  bool getDomainKeys(const ZoneName& name, std::vector<DNSBackend::KeyData>& keys) override
-  {
-    if (f_get_domain_keys == nullptr) {
-      return false;
-    }
-
-    if (d_debug_log) {
-      SLOG(g_log << Logger::Debug << "[" << getPrefix() << "] Calling " << "get_domain_keys" << "(" << "name=" << name << ")" << endl,
-           d_slog->info(Logr::Debug, "Calling get_domain_keys", "domain", Logging::Loggable(name)));
-    }
-    get_domain_keys_result_t result = f_get_domain_keys(name.operator const DNSName&());
-
-    if (result.which() == 0) {
-      return false;
-    }
-
-    for (const auto& row : boost::get<vector<pair<int, keydata_result_t>>>(result)) {
-      DNSBackend::KeyData key;
-      key.published = true;
-      for (const auto& item : row.second) {
-        if (item.first == "content") {
-          key.content = boost::get<string>(item.second);
-        }
-        else if (item.first == "id") {
-          key.id = static_cast<unsigned int>(boost::get<int>(item.second));
-        }
-        else if (item.first == "flags") {
-          key.flags = static_cast<unsigned int>(boost::get<int>(item.second));
-        }
-        else if (item.first == "active") {
-          key.active = boost::get<bool>(item.second);
-        }
-        else if (item.first == "published") {
-          key.published = boost::get<bool>(item.second);
-        }
-        else {
-          SLOG(g_log << Logger::Warning << "[" << getPrefix() << "] Unsupported key '" << item.first << "' in keydata result" << endl,
-               d_slog->info(Logr::Warning, "Unsupported key in keydata result", "key", Logging::Loggable(item.first)));
-        }
-      }
-      if (d_debug_log) {
-        SLOG(g_log << Logger::Debug << "[" << getPrefix() << "] Got result " << "'" << "id=" << key.id << ",flags=" << key.flags << ",active=" << (key.active ? "true" : "false") << ",published=" << (key.published ? "true" : "false") << "'" << endl,
-             d_slog->info(Logr::Debug, "Got result", "id", Logging::Loggable(key.id), "flags", Logging::Loggable(key.flags), "active", Logging::Loggable(key.active ? "true" : "false"), "published", Logging::Loggable(key.published ? "true" : "false")));
-      }
-      keys.emplace_back(std::move(key));
-    }
-
-    return true;
-  }
-
-  bool getBeforeAndAfterNamesAbsolute(domainid_t id, const DNSName& qname, DNSName& unhashed, DNSName& before, DNSName& after) override
-  {
-    if (f_get_before_and_after_names_absolute == nullptr) {
-      return false;
-    }
-
-    if (d_debug_log) {
-      SLOG(g_log << Logger::Debug << "[" << getPrefix() << "] Calling " << "get_before_and_after_names_absolute" << "(" << "id=<<" << id << ",qname=" << qname << ")" << endl,
-           d_slog->info(Logr::Debug, "Calling get_before_and_after_names_absolute", "id", Logging::Loggable(id), "name", Logging::Loggable(qname)));
-    }
-    get_before_and_after_names_absolute_result_t result = f_get_before_and_after_names_absolute(id, qname);
-
-    if (result.which() == 0) {
-      return false;
-    }
-
-    before_and_after_names_result_t row = boost::get<before_and_after_names_result_t>(result);
-    if (row.size() != 3) {
-      SLOG(g_log << Logger::Error << "Invalid result from dns_get_before_and_after_names_absolute, expected array with 3 items, got " << row.size() << "item(s)" << endl,
-           d_slog->info(Logr::Error, "Invalid result from dns_get_before_and_after_names_absolute, expected array with 3 rows", "rows returned", Logging::Loggable(row.size())));
-      return false;
-    }
-    for (const auto& item : row) {
-      DNSName value;
-      if (item.second.which() == 0) {
-        value = DNSName(boost::get<string>(item.second));
-      }
-      else {
-        value = DNSName(boost::get<DNSName>(item.second));
-      }
-      if (item.first == "unhashed") {
-        unhashed = std::move(value);
-      }
-      else if (item.first == "before") {
-        before = std::move(value);
-      }
-      else if (item.first == "after") {
-        after = std::move(value);
-      }
-      else {
-        SLOG(g_log << Logger::Error << "Invalid result from dns_get_before_and_after_names_absolute, unexpected key " << item.first << endl,
-             d_slog->info(Logr::Error, "Invalid result from dns_get_before_and_after_names_absolute, unexpected key", "key", Logging::Loggable(item.first)));
-        return false;
-      }
-    }
-
-    if (d_debug_log) {
-      SLOG(g_log << Logger::Debug << "[" << getPrefix() << "] Got result " << "'" << "unhashed=" << unhashed << ",before=" << before << ",after=" << after << "'" << endl,
-           d_slog->info(Logr::Debug, "Got result", "unhashed", Logging::Loggable(unhashed), "before", Logging::Loggable(before), "after", Logging::Loggable(after)));
-    }
-    return true;
-  }
+  void postPrepareContext() override;
+  void postLoad() override;
+  unsigned int getCapabilities() override;
+  bool list(const ZoneName& target, domainid_t domain_id, bool /* include_disabled */ = false) override;
+  void lookup(const QType& qtype, const DNSName& qname, domainid_t domain_id, DNSPacket* p = nullptr) override;
+  bool get(DNSResourceRecord& rr) override;
+  string directBackendCmd(const string& querystr) override;
+  void setNotified(domainid_t id, uint32_t serial) override;
+  void parseDomainInfo(const domaininfo_result_t& row, DomainInfo& di);
+  bool getDomainInfo(const ZoneName& domain, DomainInfo& di, bool /* getSerial */ = true) override;
+  void getAllDomains(vector<DomainInfo>* domains, bool /* getSerial */, bool /* include_disabled */) override;
+  bool getAllDomainMetadata(const ZoneName& name, std::map<std::string, std::vector<std::string>>& meta) override;
+  bool getDomainMetadata(const ZoneName& name, const std::string& kind, std::vector<std::string>& meta) override;
+  bool getDomainKeys(const ZoneName& name, std::vector<DNSBackend::KeyData>& keys) override;
+  bool getBeforeAndAfterNamesAbsolute(domainid_t id, const DNSName& qname, DNSName& unhashed, DNSName& before, DNSName& after) override;
 
 private:
   std::list<DNSResourceRecord> d_result;
@@ -568,4 +105,6 @@ private:
   set_notified_call_t f_set_notified;
 
   deinit_call_t f_deinit;
+
+  void parseLookup(const lookup_result_t& result);
 };