From: Ensar Sarajčić Date: Mon, 22 Dec 2025 13:33:58 +0000 (+0100) Subject: dnsdist: replace specific MMDB queries with a single generic query X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=bd75e7132ecbbe3a417506db7d05ee00ee9d2945;p=thirdparty%2Fpdns.git dnsdist: replace specific MMDB queries with a single generic query Signed-off-by: Ensar Sarajčić --- diff --git a/pdns/dnsdistdist/dnsdist-carbon.cc b/pdns/dnsdistdist/dnsdist-carbon.cc index 60c6f894f3..d959a5fa50 100644 --- a/pdns/dnsdistdist/dnsdist-carbon.cc +++ b/pdns/dnsdistdist/dnsdist-carbon.cc @@ -19,6 +19,7 @@ * 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-lua-types.hh" #ifdef HAVE_CONFIG_H #include "config.h" #endif diff --git a/pdns/dnsdistdist/dnsdist-configuration.hh b/pdns/dnsdistdist/dnsdist-configuration.hh index 6ef8cc7d7c..a4a2c25c81 100644 --- a/pdns/dnsdistdist/dnsdist-configuration.hh +++ b/pdns/dnsdistdist/dnsdist-configuration.hh @@ -32,6 +32,7 @@ #include "credentials.hh" #include "dnsdist-actions.hh" #include "dnsdist-carbon.hh" +#include "dnsdist-lua-types.hh" #include "dnsdist-query-count.hh" #include "dnsdist-rule-chains.hh" #include "dnsdist-server-pool.hh" diff --git a/pdns/dnsdistdist/dnsdist-kvs.cc b/pdns/dnsdistdist/dnsdist-kvs.cc index bfcf155cd5..4b592fcb26 100644 --- a/pdns/dnsdistdist/dnsdist-kvs.cc +++ b/pdns/dnsdistdist/dnsdist-kvs.cc @@ -21,9 +21,12 @@ */ #include "dnsdist-kvs.hh" +#include "dnsdist-lua-types.hh" #include "dolog.hh" +#include #include +#include std::vector KeyValueLookupKeySourceIP::getKeys(const ComboAddress& addr) { @@ -304,37 +307,87 @@ bool CDBKVStore::keyExists(const std::string& key) #endif /* HAVE_CDB */ #ifdef HAVE_MMDB -bool MMDBKVStore::keyExists(const std::string& key) + +#include "ext/json11/json11.hpp" + +std::shared_ptr MMDBKVStore::getLogger() const { - auto addr = makeComboAddressFromRaw(key.size() == sizeof(in_addr) ? 4 : 6, key); - return d_mmdb->exists(addr); + return dnsdist::logging::getTopLogger("mmdb-key-value-store")->withValues("path", Logging::Loggable(d_mmdb->file_name())); } -bool MMDBKVStore::getValue(const std::string& key, std::string& value) +json11::Json MMDBKVStore::parseAny(const LuaAny& any) { - auto address = makeComboAddressFromRaw(key.size() == sizeof(in_addr) ? 4 : 6, key); - if (d_field == "country") { - return d_mmdb->queryCountry(value, address); + if (any.type() == typeid(std::string)) { + return json11::Json(boost::get(any)); } - else if (d_field == "continent") { - return d_mmdb->queryContinent(value, address); + else if (any.type() == typeid(int64_t)) { + auto val = boost::get(any); + if (val > static_cast(INT_MAX) || val < static_cast(INT_MIN)) { + SLOG(warnlog("Error while retrieving a value from MMDB database: integer overflow. Returning null."), + getLogger()->error(Logr::Warning, "", "Error while retrieving a value from MMDB database: integer overflow. Returning null.")); + return json11::Json(); + } + else { + return json11::Json(static_cast(val)); + } } - else if (d_field == "asn") { - return d_mmdb->queryAS(value, address); + else if (any.type() == typeid(uint64_t)) { + auto val = boost::get(any); + if (val > static_cast(INT_MAX)) { + SLOG(warnlog("Error while retrieving a value from MMDB database: integer overflow. Returning null."), + getLogger()->error(Logr::Warning, "", "Error while retrieving a value from MMDB database: integer overflow. Returning null.")); + return json11::Json(); + } + else { + return json11::Json(static_cast(val)); + } } - else if (d_field == "asnum") { - return d_mmdb->queryASN(value, address); + else if (any.type() == typeid(double)) { + return json11::Json(boost::get(any)); } - else if (d_field == "city") { - return d_mmdb->queryCity(value, address, "en"); + else if (any.type() == typeid(bool)) { + return json11::Json(boost::get(any)); + } + else if (any.type() == typeid(LuaArray)) { + auto luaArray = boost::get>(any); + std::vector array; + array.reserve(luaArray.size()); + for (auto& kv : luaArray) { + array.emplace_back(parseAny(kv.second)); + } + return json11::Json(array); + } + else if (any.type() == typeid(LuaAssociativeTable)) { + auto luaTable = boost::get>(any); + std::unordered_map map(luaTable.size()); + for (auto& kv : luaTable) { + map.emplace(kv.first, parseAny(kv.second)); + } + return json11::Json(map); } else { - return false; + return json11::Json(); } } -bool MMDBKVStore::reload() +bool MMDBKVStore::keyExists(const std::string& key) { - return true; + auto addr = makeComboAddressFromRaw(key.size() == sizeof(in_addr) ? 4 : 6, key); + return d_mmdb->exists(addr); +} +bool MMDBKVStore::getValue(const std::string& key, std::string& value) +{ + auto addr = makeComboAddressFromRaw(key.size() == sizeof(in_addr) ? 4 : 6, key); + LuaAny ret; + bool result = d_mmdb->query(ret, d_queryParams, addr); + if (ret.type() == typeid(std::string)) { + value = boost::get(ret); + return result; + } + + json11::Json json = parseAny(ret); + json.dump(value); + + return result; } #endif // HAVE_MMDB diff --git a/pdns/dnsdistdist/dnsdist-kvs.hh b/pdns/dnsdistdist/dnsdist-kvs.hh index 456e87d9bf..030e03b190 100644 --- a/pdns/dnsdistdist/dnsdist-kvs.hh +++ b/pdns/dnsdistdist/dnsdist-kvs.hh @@ -24,6 +24,9 @@ #include #include "dnsdist.hh" #include "logr.hh" +#include "dnsdist-lua-types.hh" +#include "ext/json11/json11.hpp" +#include "iputils.hh" class KeyValueLookupKey { @@ -239,16 +242,22 @@ private: class MMDBKVStore : public KeyValueStore { public: - MMDBKVStore(const std::string& fname, const std::string& modeStr, const std::string& field) : - d_mmdb(std::make_unique(fname, modeStr)), d_field(field) {} + MMDBKVStore(const std::shared_ptr mmdb, const LuaTypeOrArrayOf& queryParams) : + d_mmdb(mmdb), d_originalParams(queryParams), d_queryParams(MMDB::convertParams(d_originalParams)) {}; bool keyExists(const std::string& key) override; bool getValue(const std::string& key, std::string& value) override; - bool reload() override; + bool reload() override + { + return true; + } private: - std::unique_ptr d_mmdb{nullptr}; - std::string d_field; -}; + std::shared_ptr getLogger() const; + std::shared_ptr d_mmdb; + const LuaTypeOrArrayOf d_originalParams; + const boost::variant> d_queryParams; + json11::Json parseAny(const LuaAny& any); +}; #endif // HAVE_MMDB diff --git a/pdns/dnsdistdist/dnsdist-lua-bindings-kvs.cc b/pdns/dnsdistdist/dnsdist-lua-bindings-kvs.cc index 63267bdfad..9699b18a2a 100644 --- a/pdns/dnsdistdist/dnsdist-lua-bindings-kvs.cc +++ b/pdns/dnsdistdist/dnsdist-lua-bindings-kvs.cc @@ -44,13 +44,13 @@ void setupLuaBindingsKVS([[maybe_unused]] LuaContext& luaCtx, [[maybe_unused]] b #endif /* HAVE_CDB */ #ifdef HAVE_MMDB - luaCtx.writeFunction("newMMDBKVStore", [client](const std::string& fname, const std::string& field, std::optional mmap) { + luaCtx.writeFunction("newMMDBKVStore", [client](const std::shared_ptr& mmdb, const LuaTypeOrArrayOf& queryParams) { if (client) { return std::shared_ptr(nullptr); } - return std::shared_ptr(new MMDBKVStore(fname, mmap ? "mmap" : "", field)); + return std::shared_ptr(new MMDBKVStore(mmdb, queryParams)); }); -#endif /* HAVE_MMDB */ +#endif // HAVE_MMDB #if defined(HAVE_LMDB) || defined(HAVE_CDB) || defined(HAVE_MMDB) /* Key Value Store objects */ diff --git a/pdns/dnsdistdist/dnsdist-lua-bindings-mmdb.cc b/pdns/dnsdistdist/dnsdist-lua-bindings-mmdb.cc index 8761a4584e..79f8f93992 100644 --- a/pdns/dnsdistdist/dnsdist-lua-bindings-mmdb.cc +++ b/pdns/dnsdistdist/dnsdist-lua-bindings-mmdb.cc @@ -19,6 +19,7 @@ * 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-lua-types.hh" #include "dnsdist-lua.hh" #include "iputils.hh" #include @@ -33,109 +34,33 @@ void setupLuaBindingsMMDB([[maybe_unused]] LuaContext& luaCtx) bool mmap{false}; getOptionalValue(vars, "mmap", mmap); - auto mmdb = std::shared_ptr(new MMDB(name, mmap ? "mmap" : "")); + auto mmdb = std::make_shared(name, mmap ? "mmap" : ""); return mmdb; }); - luaCtx.registerFunction (std::shared_ptr::*)(const ComboAddress&)>("queryCountry", [](std::shared_ptr& mmdb, const ComboAddress& ip) { - std::optional result{std::nullopt}; + luaCtx.registerFunction (std::shared_ptr::*)(const LuaTypeOrArrayOf&, const ComboAddress&)>("query", [](std::shared_ptr& mmdb, const LuaTypeOrArrayOf& queryParams, const ComboAddress& ip) { + std::optional result{std::nullopt}; if (!mmdb) { return result; } - std::string value; - if (mmdb->queryCountry(value, ip)) { - result = value; - } - - return result; - }); - - luaCtx.registerFunction (std::shared_ptr::*)(const ComboAddress&)>("queryContinent", [](std::shared_ptr& mmdb, const ComboAddress& ip) { - std::optional result{std::nullopt}; - if (!mmdb) { - return result; - } - - std::string value; - if (mmdb->queryContinent(value, ip)) { - result = value; - } - - return result; - }); - - luaCtx.registerFunction (std::shared_ptr::*)(const ComboAddress&)>("queryAS", [](std::shared_ptr& mmdb, const ComboAddress& ip) { - std::optional result{std::nullopt}; - if (!mmdb) { - return result; - } - - std::string value; - if (mmdb->queryAS(value, ip)) { - result = value; - } - - return result; - }); - - luaCtx.registerFunction (std::shared_ptr::*)(const ComboAddress&)>("queryASN", [](std::shared_ptr& mmdb, const ComboAddress& ip) { - std::optional result{std::nullopt}; - if (!mmdb) { - return result; - } - - std::string value; - if (mmdb->queryASN(value, ip)) { - result = value; - } - - return result; - }); - - luaCtx.registerFunction (std::shared_ptr::*)(const ComboAddress&)>("queryRegion", [](std::shared_ptr& mmdb, const ComboAddress& ip) { - std::optional result{std::nullopt}; - if (!mmdb) { - return result; - } + LuaAny value{false}; - std::string value; - if (mmdb->queryRegion(value, ip)) { + if (mmdb->query(value, MMDB::convertParams(queryParams), ip)) { result = value; } return result; }); - luaCtx.registerFunction (std::shared_ptr::*)(const ComboAddress&, const std::string&)>("queryCity", [](std::shared_ptr& mmdb, const ComboAddress& ip, const std::string& language) { - std::optional result{std::nullopt}; + luaCtx.registerFunction::*)(const ComboAddress&)>("exists", [](std::shared_ptr& mmdb, const ComboAddress& ip) { + bool result = false; if (!mmdb) { return result; } - std::string value; - if (mmdb->queryCity(value, ip, language)) { - result = value; - } - - return result; - }); - - luaCtx.registerFunction> (std::shared_ptr::*)(const ComboAddress&)>("queryLocation", [](std::shared_ptr& mmdb, const ComboAddress& ip) { - std::optional> result{std::nullopt}; - if (!mmdb) { - return result; - } - - double lat; - double lon; - int prec; - if (mmdb->queryLocation(lat, lon, prec, ip)) { - result = {lat, lon, prec}; - } - - return result; + return mmdb->exists(ip); }); #endif } diff --git a/pdns/dnsdistdist/dnsdist-lua-types.hh b/pdns/dnsdistdist/dnsdist-lua-types.hh new file mode 100644 index 0000000000..a76a91afd4 --- /dev/null +++ b/pdns/dnsdistdist/dnsdist-lua-types.hh @@ -0,0 +1,39 @@ +/* + * 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. + */ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +template +using LuaArray = std::vector>; +template +using LuaAssociativeTable = std::unordered_map; +template +using LuaTypeOrArrayOf = boost::variant>; +using LuaAny = boost::make_recursive_variant, LuaAssociativeTable>::type; diff --git a/pdns/dnsdistdist/dnsdist-lua.hh b/pdns/dnsdistdist/dnsdist-lua.hh index b07bc8b1cf..5f9e57d68d 100644 --- a/pdns/dnsdistdist/dnsdist-lua.hh +++ b/pdns/dnsdistdist/dnsdist-lua.hh @@ -23,19 +23,13 @@ #include "dolog.hh" #include "dnsdist.hh" +#include "dnsdist-lua-types.hh" #include "ext/luawrapper/include/LuaContext.hpp" extern RecursiveLockGuarded g_lua; extern std::string g_outputBuffer; // locking for this is ok, as locked by g_luamutex -template -using LuaArray = std::vector>; -template -using LuaAssociativeTable = std::unordered_map; -template -using LuaTypeOrArrayOf = boost::variant>; - using luaruleparams_t = LuaAssociativeTable; using luadnsrule_t = boost::variant, std::shared_ptr, DNSName, LuaArray>; diff --git a/pdns/dnsdistdist/dnsdist-web.cc b/pdns/dnsdistdist/dnsdist-web.cc index 1afb9750a7..1de7074e80 100644 --- a/pdns/dnsdistdist/dnsdist-web.cc +++ b/pdns/dnsdistdist/dnsdist-web.cc @@ -27,6 +27,7 @@ #include #include +#include "dnsdist-lua-types.hh" #include "ext/json11/json11.hpp" #include diff --git a/pdns/dnsdistdist/mmdb.cc b/pdns/dnsdistdist/mmdb.cc index 1273ef28b9..19c5560670 100644 --- a/pdns/dnsdistdist/mmdb.cc +++ b/pdns/dnsdistdist/mmdb.cc @@ -20,6 +20,9 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "dnsdist-lua-types.hh" +#include +#include #include #ifdef HAVE_CONFIG_H #include "config.h" @@ -30,19 +33,22 @@ #include "mmdb.hh" #include -MMDB::MMDB(const std::string& fname, const std::string& modeStr) +MMDB::MMDB(const std::string& fname, const std::string& modeStr) : + d_fname(fname) { int ec; int flags = 0; - if (modeStr == "") + if (modeStr == "") { /* for the benefit of ifdef */ - ; + } #ifdef HAVE_MMAP - else if (modeStr == "mmap") + else if (modeStr == "mmap") { flags |= MMDB_MODE_MMAP; + } #endif - else + else { throw std::runtime_error(std::string("Unsupported mode ") + modeStr + ("for mmdb")); + } memset(&d_db, 0, sizeof(d_db)); if ((ec = MMDB_open(fname.c_str(), flags, &d_db)) < 0) throw std::runtime_error(std::string("Cannot open ") + fname + std::string(": ") + std::string(MMDB_strerror(ec))); @@ -50,113 +56,201 @@ MMDB::MMDB(const std::string& fname, const std::string& modeStr) dnsdist::logging::getTopLogger("mmdb")->info(Logr::Info, "Opened MMDB database", "path", Logging::Loggable(fname), "type", Logging::Loggable(d_db.metadata.database_type), "version", Logging::Loggable(std::to_string(d_db.metadata.binary_format_major_version) + "." + std::to_string(d_db.metadata.binary_format_minor_version)))); } -bool MMDB::queryCountry(string& ret, const ComboAddress& ip) +bool MMDB::query(LuaAny& ret, const boost::variant>& queryParams, const ComboAddress& ip) const { MMDB_entry_data_s data; MMDB_lookup_result_s res; - if (!mmdbLookup(ip, res)) + if (!mmdbLookup(ip, res)) { return false; - if (MMDB_get_value(&res.entry, &data, "country", "iso_code", NULL) != MMDB_SUCCESS || !data.has_data) + } + + if (auto q = boost::get(&queryParams)) { + if (MMDB_get_value(&res.entry, &data, q, NULL) != MMDB_SUCCESS || !data.has_data) + return false; + } + else if (auto params = boost::get>(&queryParams)) { + if (MMDB_aget_value(&res.entry, &data, ¶ms->at(0)) != MMDB_SUCCESS || !data.has_data) + return false; + } + + if (mmdbDecode(&data, ret)) { + return true; + } + + MMDB_entry_s data_entry{&d_db, data.offset}; + auto elistopt = getEntryList(&data_entry); + if (!elistopt) { return false; - ret = string(data.utf8_string, data.data_size); - return true; + } + auto elist = std::move(*elistopt); + auto first = elist.getFirst(); + return mmdbDecodeEntryList(&first, ret); } -bool MMDB::queryContinent(string& ret, const ComboAddress& ip) +const boost::variant> MMDB::convertParams(const LuaTypeOrArrayOf& queryParams) { - MMDB_entry_data_s data; - MMDB_lookup_result_s res; - if (!mmdbLookup(ip, res)) - return false; - if (MMDB_get_value(&res.entry, &data, "continent", "code", NULL) != MMDB_SUCCESS || !data.has_data) - return false; - ret = string(data.utf8_string, data.data_size); - return true; + if (auto param = boost::get(&queryParams)) { + return param->c_str(); + } + else if (auto params = boost::get>>(&queryParams)) { + auto paramsArray = std::vector(params->size() + 1); + for (size_t i = 0; i < params->size(); ++i) { + paramsArray.at(i) = params->at(i).second.c_str(); + } + paramsArray.at(params->size()) = NULL; + return paramsArray; + } + else { + return ""; + } } -bool MMDB::queryAS(string& ret, const ComboAddress& ip) +std::shared_ptr MMDB::getLogger() const { - MMDB_entry_data_s data; - MMDB_lookup_result_s res; - if (!mmdbLookup(ip, res)) - return false; - if (MMDB_get_value(&res.entry, &data, "autonomous_system_organization", NULL) != MMDB_SUCCESS || !data.has_data) - return false; - ret = string(data.utf8_string, data.data_size); - return true; + return dnsdist::logging::getTopLogger("mmdb")->withValues("path", Logging::Loggable(d_fname)); } -bool MMDB::queryASN(string& ret, const ComboAddress& ip) +bool MMDB::mmdbDecode(MMDB_entry_data_s* data, LuaAny& ret) const { - MMDB_entry_data_s data; - MMDB_lookup_result_s res; - if (!mmdbLookup(ip, res)) + switch (data->type) { + case MMDB_DATA_TYPE_BOOLEAN: + ret = data->boolean; + break; + case MMDB_DATA_TYPE_UTF8_STRING: + ret = string(data->utf8_string, data->data_size); + break; + case MMDB_DATA_TYPE_DOUBLE: + ret = data->double_value; + break; + case MMDB_DATA_TYPE_FLOAT: + ret = data->float_value; + break; + case MMDB_DATA_TYPE_INT32: + ret = static_cast(data->int32); + break; + case MMDB_DATA_TYPE_UINT16: + ret = static_cast(data->uint16); + break; + case MMDB_DATA_TYPE_UINT32: + ret = static_cast(data->uint32); + break; + case MMDB_DATA_TYPE_UINT64: + ret = static_cast(data->uint64); + break; + default: return false; - if (MMDB_get_value(&res.entry, &data, "autonomous_system_number", NULL) != MMDB_SUCCESS || !data.has_data) - return false; - ret = std::to_string(data.uint32); + } return true; } -bool MMDB::queryRegion(string& ret, const ComboAddress& ip) +bool MMDB::mmdbDecodeEntryList(MMDB_entry_data_list_s** data, LuaAny& ret) const { - MMDB_entry_data_s data; - MMDB_lookup_result_s res; - if (!mmdbLookup(ip, res)) + switch ((*data)->entry_data.type) { + case MMDB_DATA_TYPE_BOOLEAN: + case MMDB_DATA_TYPE_UTF8_STRING: + case MMDB_DATA_TYPE_DOUBLE: + case MMDB_DATA_TYPE_FLOAT: + case MMDB_DATA_TYPE_INT32: + case MMDB_DATA_TYPE_UINT16: + case MMDB_DATA_TYPE_UINT32: + case MMDB_DATA_TYPE_UINT64: + return mmdbDecode(&((*data)->entry_data), ret); + case MMDB_DATA_TYPE_ARRAY: + return mmdbDecodeArray(data, ret); + break; + case MMDB_DATA_TYPE_MAP: + return mmdbDecodeMap(data, ret); + break; + default: return false; - if (MMDB_get_value(&res.entry, &data, "subdivisions", "0", "iso_code", NULL) != MMDB_SUCCESS || !data.has_data) - return false; - ret = string(data.utf8_string, data.data_size); - return true; + } } -bool MMDB::queryCity(string& ret, const ComboAddress& ip, const string& language) +bool MMDB::mmdbDecodeMap(MMDB_entry_data_list_s** data, LuaAny& ret) const { - MMDB_entry_data_s data; - MMDB_lookup_result_s res; - if (!mmdbLookup(ip, res)) - return false; - if ((MMDB_get_value(&res.entry, &data, "cities", "0", NULL) != MMDB_SUCCESS || !data.has_data) && (MMDB_get_value(&res.entry, &data, "city", "names", language.c_str(), NULL) != MMDB_SUCCESS || !data.has_data)) - return false; - ret = string(data.utf8_string, data.data_size); + LuaAssociativeTable result; + + MMDB_entry_data_list_s* this_data = *data; + + for (auto size = this_data->entry_data.data_size; size > 0; --size) { + *data = (*data)->next; + + if (!*data) { + break; + } + + if ((*data)->entry_data.type != MMDB_DATA_TYPE_UTF8_STRING) { + // Invalid key, stop decoding + return false; + } + + std::string key{(*data)->entry_data.utf8_string, (*data)->entry_data.data_size}; + + *data = (*data)->next; + if (!*data) { + break; + } + + LuaAny value; + if (!mmdbDecodeEntryList(data, value)) { + // Failed value decoding, stop decoding + return false; + } + + result.emplace(std::move(key), std::move(value)); + } + + ret = result; return true; } -bool MMDB::queryLocation(double& latitude, double& longitude, - int& prec, - const ComboAddress& ip) +bool MMDB::mmdbDecodeArray(MMDB_entry_data_list_s** data, LuaAny& ret) const { - MMDB_entry_data_s data; - MMDB_lookup_result_s res; - if (!mmdbLookup(ip, res)) - return false; - if (MMDB_get_value(&res.entry, &data, "location", "latitude", NULL) != MMDB_SUCCESS || !data.has_data) - return false; - latitude = data.double_value; - if (MMDB_get_value(&res.entry, &data, "location", "longitude", NULL) != MMDB_SUCCESS || !data.has_data) - return false; - longitude = data.double_value; - if (MMDB_get_value(&res.entry, &data, "location", "accuracy_radius", NULL) != MMDB_SUCCESS || !data.has_data) - return false; - prec = data.uint16; + LuaArray result; + + MMDB_entry_data_list_s* this_data = *data; + + for (uint32_t i = 0; i < this_data->entry_data.data_size; ++i) { + *data = (*data)->next; + + if (!*data) { + break; + } + + LuaAny value; + if (!mmdbDecodeEntryList(data, value)) { + // Failed value decoding, stop decoding + return false; + } + + result.emplace_back(i + 1, std::move(value)); + } + + ret = result; return true; } -bool MMDB::mmdbLookup(const ComboAddress& ip, MMDB_lookup_result_s& res) +bool MMDB::mmdbLookup(const ComboAddress& ip, MMDB_lookup_result_s& res) const { int mmdb_ec = 0; res = MMDB_lookup_sockaddr(&d_db, reinterpret_cast(&ip), &mmdb_ec); if (mmdb_ec != MMDB_SUCCESS) { - VERBOSESLOG(infolog("mmdbLookup(%s) failed: %s", ip.toString(), MMDB_strerror(mmdb_ec)), dnsdist::logging::getTopLogger("mmdb")->error(Logr::Info, MMDB_strerror(mmdb_ec), "mmdbLookup failed", "ip", Logging::Loggable(ip))); + VERBOSESLOG(infolog("mmdbLookup(%s) failed: %s", ip.toString(), MMDB_strerror(mmdb_ec)), getLogger()->error(Logr::Info, MMDB_strerror(mmdb_ec), "mmdbLookup failed", "ip", Logging::Loggable(ip))); } else if (res.found_entry) { - // gl.netmask = res.netmask; - // /* If it's a IPv6 database, IPv4 netmasks are reduced from 128, so we need to deduct - // 96 to get from [96,128] => [0,32] range */ - // if (!v6 && gl.netmask > 32) - // gl.netmask -= 96; return true; } return false; } + +std::optional MMDB::getEntryList(MMDB_entry_s* entry) const +{ + MMDB_entry_data_list_s* entry_data_list; + int status = MMDB_get_entry_data_list(entry, &entry_data_list); + + if (status != MMDB_SUCCESS) { + return std::nullopt; + } + return {entry_data_list}; +} diff --git a/pdns/dnsdistdist/mmdb.hh b/pdns/dnsdistdist/mmdb.hh index 5b8792406b..bfab18fc86 100644 --- a/pdns/dnsdistdist/mmdb.hh +++ b/pdns/dnsdistdist/mmdb.hh @@ -21,32 +21,59 @@ */ #pragma once +#include "dnsdist-lua-types.hh" #include "iputils.hh" #include +#include #include +class MMDBEntryList; + class MMDB { public: MMDB(const std::string& fname, const std::string& modeStr); - bool queryCountry(std::string& ret, const ComboAddress& ip); - bool queryContinent(std::string& ret, const ComboAddress& ip); - bool queryAS(std::string& ret, const ComboAddress& ip); - bool queryASN(std::string& ret, const ComboAddress& ip); - bool queryRegion(std::string& ret, const ComboAddress& ip); - bool queryCity(std::string& ret, const ComboAddress& ip, const std::string& language); - bool queryLocation(double& latitude, double& longitude, int& prec, const ComboAddress& ip); - bool exists(const ComboAddress& ip) + static const boost::variant> convertParams(const LuaTypeOrArrayOf& queryParams); + bool query(LuaAny& ret, const boost::variant>& queryParams, const ComboAddress& ip) const; + bool exists(const ComboAddress& ip) const { MMDB_lookup_result_s res; return mmdbLookup(ip, res); } + const std::string& file_name() const + { + return d_fname; + } ~MMDB() { MMDB_close(&d_db); }; private: + std::string d_fname; MMDB_s d_db; - bool mmdbLookup(const ComboAddress& ip, MMDB_lookup_result_s& res); + std::shared_ptr getLogger() const; + // Decodes one of the basic types (no arrays and maps) + bool mmdbDecode(MMDB_entry_data_s* data, LuaAny& ret) const; + // Decodes whole entry data list (supports arrays and maps too) + bool mmdbDecodeEntryList(MMDB_entry_data_list_s** data, LuaAny& ret) const; + bool mmdbDecodeMap(MMDB_entry_data_list_s** data, LuaAny& ret) const; + bool mmdbDecodeArray(MMDB_entry_data_list_s** data, LuaAny& ret) const; + bool mmdbLookup(const ComboAddress& ip, MMDB_lookup_result_s& res) const; + std::optional getEntryList(MMDB_entry_s* entry) const; +}; + +class MMDBEntryList +{ +public: + MMDBEntryList(MMDB_entry_data_list_s* first) : + d_entry_list_first(first, MMDB_free_entry_data_list) {} + + MMDB_entry_data_list_s* getFirst() const + { + return d_entry_list_first.get(); + } + +private: + std::unique_ptr d_entry_list_first; };