/* * 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 "dnsdist.hh" class KeyValueLookupKey { public: virtual ~KeyValueLookupKey() { } virtual std::vector getKeys(const DNSQuestion&) = 0; virtual std::string toString() const = 0; }; class KeyValueLookupKeySourceIP: public KeyValueLookupKey { public: KeyValueLookupKeySourceIP(uint8_t v4Mask, uint8_t v6Mask, bool includePort): d_v4Mask(v4Mask), d_v6Mask(v6Mask), d_includePort(includePort) { } std::vector getKeys(const ComboAddress& addr); std::vector getKeys(const DNSQuestion& dq) override { return getKeys(*dq.remote); } std::string toString() const override { return "source IP (masked to " + std::to_string(d_v4Mask) + " (v4) / " + std::to_string(d_v6Mask) + " (v6) bits)" + (d_includePort ? " including the port" : ""); } private: uint8_t d_v4Mask; uint8_t d_v6Mask; bool d_includePort; }; class KeyValueLookupKeyQName: public KeyValueLookupKey { public: KeyValueLookupKeyQName(bool wireFormat): d_wireFormat(wireFormat) { } std::vector getKeys(const DNSName& qname) { if (d_wireFormat) { return {qname.toDNSStringLC()}; } return {qname.makeLowerCase().toStringRootDot()}; } std::vector getKeys(const DNSQuestion& dq) override { return getKeys(*dq.qname); } std::string toString() const override { if (d_wireFormat) { return "qname in wire format"; } return "qname"; } private: bool d_wireFormat; }; class KeyValueLookupKeySuffix: public KeyValueLookupKey { public: KeyValueLookupKeySuffix(size_t minLabels, bool wireFormat): d_minLabels(minLabels), d_wireFormat(wireFormat) { } std::vector getKeys(const DNSName& qname); std::vector getKeys(const DNSQuestion& dq) override { return getKeys(*dq.qname); } std::string toString() const override { if (d_minLabels > 0) { return "suffix " + std::string(d_wireFormat ? "in wire format " : "") + "with at least " + std::to_string(d_minLabels) + " label(s)"; } return "suffix" + std::string(d_wireFormat ? " in wire format" : ""); } private: size_t d_minLabels; bool d_wireFormat; }; class KeyValueLookupKeyTag: public KeyValueLookupKey { public: KeyValueLookupKeyTag(const std::string& tag): d_tag(tag) { } std::vector getKeys(const DNSQuestion& dq) override { if (dq.qTag) { const auto& it = dq.qTag->find(d_tag); if (it != dq.qTag->end()) { return { it->second }; } } return {}; } std::string toString() const override { return "value of the tag named '" + d_tag + "'"; } private: std::string d_tag; }; class KeyValueStore { public: virtual ~KeyValueStore() { } virtual bool keyExists(const std::string& key) = 0; virtual bool getValue(const std::string& key, std::string& value) = 0; // do a range-based lookup (mostly useful for IP addresses), assuming that: // there is a key for the last element of the range (2001:0db8:ffff:ffff:ffff:ffff:ffff:ffff, in network byte order, for 2001:db8::/32) // which contains the first element of the range (2001:0db8:0000:0000:0000:0000:0000:0000, in network bytes order) followed by any data in the value // AND there is no overlapping ranges in the database !! // This requires that the underlying store supports ordered keys, which is true for LMDB but not for CDB, for example. virtual bool getRangeValue(const std::string& key, std::string& value) { throw std::runtime_error("range-based lookups are not implemented for this Key-Value Store"); } virtual bool reload() { return false; } }; #ifdef HAVE_LMDB #include "ext/lmdb-safe/lmdb-safe.hh" class LMDBKVStore: public KeyValueStore { public: LMDBKVStore(const std::string& fname, const std::string& dbName, bool noLock=false): d_env(fname.c_str(), noLock ? MDB_NOSUBDIR|MDB_RDONLY|MDB_NOLOCK : MDB_NOSUBDIR|MDB_RDONLY, 0600), d_dbi(d_env.openDB(dbName, 0)), d_fname(fname), d_dbName(dbName) { } bool keyExists(const std::string& key) override; bool getValue(const std::string& key, std::string& value) override; bool getRangeValue(const std::string& key, std::string& value) override; private: MDBEnv d_env; MDBDbi d_dbi; std::string d_fname; std::string d_dbName; }; #endif /* HAVE_LMDB */ #ifdef HAVE_CDB #include "cdb.hh" class CDBKVStore: public KeyValueStore { public: CDBKVStore(const std::string& fname, time_t refreshDelay); ~CDBKVStore(); bool keyExists(const std::string& key) override; bool getValue(const std::string& key, std::string& value) override; bool reload() override; private: void refreshDBIfNeeded(time_t now); bool reload(const struct stat& st); SharedLockGuarded> d_cdb{nullptr}; std::string d_fname; time_t d_mtime{0}; time_t d_nextCheck{0}; time_t d_refreshDelay{0}; std::atomic_flag d_refreshing; }; #endif /* HAVE_LMDB */