return res;
}
+bool generateDNSCryptCertificate(const std::string& providerPrivateKeyFile, uint32_t serial, time_t begin, time_t end, DnsCryptCert& certOut, DnsCryptPrivateKey& keyOut)
+{
+ bool success = false;
+ unsigned char providerPrivateKey[DNSCRYPT_PROVIDER_PRIVATE_KEY_SIZE];
+ sodium_mlock(providerPrivateKey, sizeof(providerPrivateKey));
+ sodium_memzero(providerPrivateKey, sizeof(providerPrivateKey));
+
+ try {
+ ifstream providerKStream(providerPrivateKeyFile);
+ providerKStream.read((char*) providerPrivateKey, sizeof(providerPrivateKey));
+ if (providerKStream.fail()) {
+ providerKStream.close();
+ throw std::runtime_error("Invalid DNSCrypt provider key file " + providerPrivateKeyFile);
+ }
+
+ DnsCryptContext::generateCertificate(serial, begin, end, providerPrivateKey, keyOut, certOut);
+ success = true;
+ }
+ catch(const std::exception& e) {
+ errlog(e.what());
+ }
+
+ sodium_memzero(providerPrivateKey, sizeof(providerPrivateKey));
+ sodium_munlock(providerPrivateKey, sizeof(providerPrivateKey));
+ return success;
+}
+
#endif
bool hasOldCert{false};
};
+bool generateDNSCryptCertificate(const std::string& providerPrivateKeyFile, uint32_t serial, time_t begin, time_t end, DnsCryptCert& certOut, DnsCryptPrivateKey& keyOut);
+
#endif
/* keyword, function, parameters, description */
{ "addACL", true, "netmask", "add to the ACL set who can use this server" },
{ "addAction", true, "DNS rule, DNS action", "add a rule" },
- { "addAnyTCRule", true, "", "(deprecated) generate TC=1 answers to ANY queries received over UDP, moving them to TCP" },
- { "addDelay", true, "domain, n", "(deprecated) delay answers within that domain by n milliseconds" },
- { "addDisableValidationRule", true, "DNS rule", "(deprecated) set the CD flags to 1 for all queries matching the specified domain" },
{ "addDNSCryptBind", true, "\"127.0.0.1:8443\", \"provider name\", \"/path/to/resolver.cert\", \"/path/to/resolver.key\", {reusePort=false, tcpFastOpenSize=0, interface=\"\", cpus={}}", "listen to incoming DNSCrypt queries on 127.0.0.1 port 8443, with a provider name of `provider name`, using a resolver certificate and associated key stored respectively in the `resolver.cert` and `resolver.key` files. The fifth optional parameter is a table of parameters" },
- { "addDomainBlock", true, "domain", "(deprecated) block queries within this domain" },
- { "addDomainSpoof", true, "domain, ip[, ip6]", "(deprecated) generate answers for A/AAAA/ANY queries using the ip parameters" },
{ "addDynBlocks", true, "addresses, message[, seconds[, action]]", "block the set of addresses with message `msg`, for `seconds` seconds (10 by default), applying `action` (default to the one set with `setDynBlocksAction()`)" },
{ "addLocal", true, "addr [, {doTCP=true, reusePort=false, tcpFastOpenSize=0, interface=\"\", cpus={}}]", "add `addr` to the list of addresses we listen on" },
{ "addLuaAction", true, "x, func", "where 'x' is all the combinations from `addAction`, and func is a function with the parameter `dq`, which returns an action to be taken on this packet. Good for rare packets but where you want to do a lot of processing" },
{ "addLuaResponseAction", true, "x, func", "where 'x' is all the combinations from `addAction`, and func is a function with the parameter `dr`, which returns an action to be taken on this response packet. Good for rare packets but where you want to do a lot of processing" },
- { "addNoRecurseRule", true, "domain", "(deprecated) clear the RD flag for all queries matching the specified domain" },
- { "addPoolRule", true, "domain, pool", "(deprecated) send queries to this domain to that pool" },
- { "addQPSLimit", true, "domain, n", "(deprecated) limit queries within that domain to n per second" },
- { "addQPSPoolRule", true, "x, limit, pool", "(deprecated) like `addPoolRule`, but only select at most 'limit' queries/s for this pool, letting the subsequent rules apply otherwise" },
{ "addCacheHitResponseAction", true, "DNS rule, DNS response action", "add a cache hit response rule" },
{ "addResponseAction", true, "DNS rule, DNS response action", "add a response rule" },
{ "AllowAction", true, "", "let these packets go through" },
--- /dev/null
+/*
+ * 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-ecs.hh"
+#include "dnsdist-lua.hh"
+#include "dnsdist-protobuf.hh"
+
+#include "dolog.hh"
+#include "ednsoptions.hh"
+#include "remote_logger.hh"
+
+class DropAction : public DNSAction
+{
+public:
+ DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
+ {
+ return Action::Drop;
+ }
+ string toString() const override
+ {
+ return "drop";
+ }
+};
+
+class AllowAction : public DNSAction
+{
+public:
+ DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
+ {
+ return Action::Allow;
+ }
+ string toString() const override
+ {
+ return "allow";
+ }
+};
+
+
+class QPSAction : public DNSAction
+{
+public:
+ QPSAction(int limit) : d_qps(limit, limit)
+ {}
+ DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
+ {
+ if(d_qps.check())
+ return Action::None;
+ else
+ return Action::Drop;
+ }
+ string toString() const override
+ {
+ return "qps limit to "+std::to_string(d_qps.getRate());
+ }
+private:
+ QPSLimiter d_qps;
+};
+
+class DelayAction : public DNSAction
+{
+public:
+ DelayAction(int msec) : d_msec(msec)
+ {}
+ DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
+ {
+ *ruleresult=std::to_string(d_msec);
+ return Action::Delay;
+ }
+ string toString() const override
+ {
+ return "delay by "+std::to_string(d_msec)+ " msec";
+ }
+private:
+ int d_msec;
+};
+
+
+class TeeAction : public DNSAction
+{
+public:
+ TeeAction(const ComboAddress& ca, bool addECS=false);
+ ~TeeAction() override;
+ DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override;
+ string toString() const override;
+ std::unordered_map<string, double> getStats() const override;
+
+private:
+ ComboAddress d_remote;
+ std::thread d_worker;
+ void worker();
+
+ int d_fd;
+ mutable std::atomic<unsigned long> d_senderrors{0};
+ unsigned long d_recverrors{0};
+ mutable std::atomic<unsigned long> d_queries{0};
+ unsigned long d_responses{0};
+ unsigned long d_nxdomains{0};
+ unsigned long d_servfails{0};
+ unsigned long d_refuseds{0};
+ unsigned long d_formerrs{0};
+ unsigned long d_notimps{0};
+ unsigned long d_noerrors{0};
+ mutable unsigned long d_tcpdrops{0};
+ unsigned long d_otherrcode{0};
+ std::atomic<bool> d_pleaseQuit{false};
+ bool d_addECS{false};
+};
+
+TeeAction::TeeAction(const ComboAddress& ca, bool addECS) : d_remote(ca), d_addECS(addECS)
+{
+ d_fd=SSocket(d_remote.sin4.sin_family, SOCK_DGRAM, 0);
+ SConnect(d_fd, d_remote);
+ setNonBlocking(d_fd);
+ d_worker=std::thread(std::bind(&TeeAction::worker, this));
+}
+
+TeeAction::~TeeAction()
+{
+ d_pleaseQuit=true;
+ close(d_fd);
+ d_worker.join();
+}
+
+
+DNSAction::Action TeeAction::operator()(DNSQuestion* dq, string* ruleresult) const
+{
+ if(dq->tcp) {
+ d_tcpdrops++;
+ }
+ else {
+ ssize_t res;
+ d_queries++;
+
+ if(d_addECS) {
+ std::string query;
+ uint16_t len = dq->len;
+ bool ednsAdded = false;
+ bool ecsAdded = false;
+ query.reserve(dq->size);
+ query.assign((char*) dq->dh, len);
+
+ if (!handleEDNSClientSubnet((char*) query.c_str(), query.capacity(), dq->qname->wirelength(), &len, &ednsAdded, &ecsAdded, *dq->remote, dq->ecsOverride, dq->ecsPrefixLength)) {
+ return DNSAction::Action::None;
+ }
+
+ res = send(d_fd, query.c_str(), len, 0);
+ }
+ else {
+ res = send(d_fd, (char*)dq->dh, dq->len, 0);
+ }
+
+ if (res <= 0)
+ d_senderrors++;
+ }
+ return DNSAction::Action::None;
+}
+
+string TeeAction::toString() const
+{
+ return "tee to "+d_remote.toStringWithPort();
+}
+
+std::unordered_map<string,double> TeeAction::getStats() const
+{
+ return {{"queries", d_queries},
+ {"responses", d_responses},
+ {"recv-errors", d_recverrors},
+ {"send-errors", d_senderrors},
+ {"noerrors", d_noerrors},
+ {"nxdomains", d_nxdomains},
+ {"refuseds", d_refuseds},
+ {"servfails", d_servfails},
+ {"other-rcode", d_otherrcode},
+ {"tcp-drops", d_tcpdrops}
+ };
+}
+
+void TeeAction::worker()
+{
+ char packet[1500];
+ int res=0;
+ struct dnsheader* dh=(struct dnsheader*)packet;
+ for(;;) {
+ res=waitForData(d_fd, 0, 250000);
+ if(d_pleaseQuit)
+ break;
+ if(res < 0) {
+ usleep(250000);
+ continue;
+ }
+ if(res==0)
+ continue;
+ res=recv(d_fd, packet, sizeof(packet), 0);
+ if(res <= (int)sizeof(struct dnsheader))
+ d_recverrors++;
+ else if(res > 0)
+ d_responses++;
+
+ if(dh->rcode == RCode::NoError)
+ d_noerrors++;
+ else if(dh->rcode == RCode::ServFail)
+ d_servfails++;
+ else if(dh->rcode == RCode::NXDomain)
+ d_nxdomains++;
+ else if(dh->rcode == RCode::Refused)
+ d_refuseds++;
+ else if(dh->rcode == RCode::FormErr)
+ d_formerrs++;
+ else if(dh->rcode == RCode::NotImp)
+ d_notimps++;
+ }
+}
+
+class PoolAction : public DNSAction
+{
+public:
+ PoolAction(const std::string& pool) : d_pool(pool) {}
+ DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
+ {
+ *ruleresult=d_pool;
+ return Action::Pool;
+ }
+ string toString() const override
+ {
+ return "to pool "+d_pool;
+ }
+
+private:
+ string d_pool;
+};
+
+
+class QPSPoolAction : public DNSAction
+{
+public:
+ QPSPoolAction(unsigned int limit, const std::string& pool) : d_qps(limit, limit), d_pool(pool) {}
+ DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
+ {
+ if(d_qps.check()) {
+ *ruleresult=d_pool;
+ return Action::Pool;
+ }
+ else
+ return Action::None;
+ }
+ string toString() const override
+ {
+ return "max " +std::to_string(d_qps.getRate())+" to pool "+d_pool;
+ }
+
+private:
+ QPSLimiter d_qps;
+ string d_pool;
+};
+
+class RCodeAction : public DNSAction
+{
+public:
+ RCodeAction(int rcode) : d_rcode(rcode) {}
+ DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
+ {
+ dq->dh->rcode = d_rcode;
+ dq->dh->qr = true; // for good measure
+ return Action::HeaderModify;
+ }
+ string toString() const override
+ {
+ return "set rcode "+std::to_string(d_rcode);
+ }
+
+private:
+ int d_rcode;
+};
+
+class TCAction : public DNSAction
+{
+public:
+ DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
+ {
+ return Action::Truncate;
+ }
+ string toString() const override
+ {
+ return "tc=1 answer";
+ }
+};
+
+DNSAction::Action SpoofAction::operator()(DNSQuestion* dq, string* ruleresult) const
+{
+ uint16_t qtype = dq->qtype;
+ // do we even have a response?
+ if(d_cname.empty() && !std::count_if(d_addrs.begin(), d_addrs.end(), [qtype](const ComboAddress& a)
+ {
+ return (qtype == QType::ANY || ((a.sin4.sin_family == AF_INET && qtype == QType::A) ||
+ (a.sin4.sin_family == AF_INET6 && qtype == QType::AAAA)));
+ }))
+ return Action::None;
+
+ vector<ComboAddress> addrs;
+ unsigned int totrdatalen=0;
+ if (!d_cname.empty()) {
+ qtype = QType::CNAME;
+ totrdatalen += d_cname.toDNSString().size();
+ } else {
+ 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;
+ }
+ 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);
+ }
+ }
+
+ if(addrs.size() > 1)
+ random_shuffle(addrs.begin(), addrs.end());
+
+ unsigned int consumed=0;
+ DNSName ignore((char*)dq->dh, dq->len, sizeof(dnsheader), false, 0, 0, &consumed);
+
+ if (dq->size < (sizeof(dnsheader) + consumed + 4 + ((d_cname.empty() ? 0 : 1) + addrs.size())*12 /* recordstart */ + totrdatalen)) {
+ return Action::None;
+ }
+
+ dq->len = sizeof(dnsheader) + consumed + 4; // there goes your EDNS
+ char* dest = ((char*)dq->dh) + dq->len;
+
+ dq->dh->qr = true; // for good measure
+ dq->dh->ra = dq->dh->rd; // for good measure
+ dq->dh->ad = false;
+ dq->dh->ancount = 0;
+ dq->dh->arcount = 0; // for now, forget about your EDNS, we're marching over it
+
+ if(qtype == QType::CNAME) {
+ string wireData = d_cname.toDNSString(); // Note! This doesn't do compression!
+ const unsigned char recordstart[]={0xc0, 0x0c, // compressed name
+ 0, (unsigned char) qtype,
+ 0, QClass::IN, // IN
+ 0, 0, 0, 60, // TTL
+ 0, (unsigned char)wireData.length()};
+ static_assert(sizeof(recordstart) == 12, "sizeof(recordstart) must be equal to 12, otherwise the above check is invalid");
+
+ memcpy(dest, recordstart, sizeof(recordstart));
+ dest += sizeof(recordstart);
+ memcpy(dest, wireData.c_str(), wireData.length());
+ dq->len += wireData.length() + sizeof(recordstart);
+ dq->dh->ancount++;
+ }
+ else {
+ for(const auto& addr : addrs) {
+ unsigned char rdatalen = addr.sin4.sin_family == AF_INET ? sizeof(addr.sin4.sin_addr.s_addr) : sizeof(addr.sin6.sin6_addr.s6_addr);
+ const unsigned char recordstart[]={0xc0, 0x0c, // compressed name
+ 0, (unsigned char) (addr.sin4.sin_family == AF_INET ? QType::A : QType::AAAA),
+ 0, QClass::IN, // IN
+ 0, 0, 0, 60, // TTL
+ 0, rdatalen};
+ static_assert(sizeof(recordstart) == 12, "sizeof(recordstart) must be equal to 12, otherwise the above check is invalid");
+
+ memcpy(dest, recordstart, sizeof(recordstart));
+ dest += sizeof(recordstart);
+
+ memcpy(dest,
+ addr.sin4.sin_family == AF_INET ? (void*)&addr.sin4.sin_addr.s_addr : (void*)&addr.sin6.sin6_addr.s6_addr,
+ rdatalen);
+ dest += rdatalen;
+ dq->len += rdatalen + sizeof(recordstart);
+ dq->dh->ancount++;
+ }
+ }
+
+ dq->dh->ancount = htons(dq->dh->ancount);
+
+ return Action::HeaderModify;
+}
+
+class MacAddrAction : public DNSAction
+{
+public:
+ MacAddrAction(uint16_t code) : d_code(code)
+ {}
+ DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
+ {
+ if(dq->dh->arcount)
+ return Action::None;
+
+ string mac = getMACAddress(*dq->remote);
+ if(mac.empty())
+ return Action::None;
+
+ string optRData;
+ generateEDNSOption(d_code, mac, optRData);
+
+ string res;
+ generateOptRR(optRData, res);
+
+ if ((dq->size - dq->len) < res.length())
+ return Action::None;
+
+ dq->dh->arcount = htons(1);
+ char* dest = ((char*)dq->dh) + dq->len;
+ memcpy(dest, res.c_str(), res.length());
+ dq->len += res.length();
+
+ return Action::None;
+ }
+ string toString() const override
+ {
+ return "add EDNS MAC (code="+std::to_string(d_code)+")";
+ }
+private:
+ uint16_t d_code{3};
+};
+
+class NoRecurseAction : public DNSAction
+{
+public:
+ DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
+ {
+ dq->dh->rd = false;
+ return Action::None;
+ }
+ string toString() const override
+ {
+ return "set rd=0";
+ }
+};
+
+class LogAction : public DNSAction, public boost::noncopyable
+{
+public:
+ LogAction() : d_fp(0)
+ {
+ }
+ LogAction(const std::string& str, bool binary=true, bool append=false, bool buffered=true) : d_fname(str), d_binary(binary)
+ {
+ if(str.empty())
+ return;
+ if(append)
+ d_fp = fopen(str.c_str(), "a+");
+ else
+ d_fp = fopen(str.c_str(), "w");
+ if(!d_fp)
+ throw std::runtime_error("Unable to open file '"+str+"' for logging: "+string(strerror(errno)));
+ if(!buffered)
+ setbuf(d_fp, 0);
+ }
+ ~LogAction() override
+ {
+ if(d_fp)
+ fclose(d_fp);
+ }
+ DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
+ {
+ if(!d_fp) {
+ vinfolog("Packet from %s for %s %s with id %d", dq->remote->toStringWithPort(), dq->qname->toString(), QType(dq->qtype).getName(), dq->dh->id);
+ }
+ else {
+ if(d_binary) {
+ string out = dq->qname->toDNSString();
+ fwrite(out.c_str(), 1, out.size(), d_fp);
+ fwrite((void*)&dq->qtype, 1, 2, d_fp);
+ }
+ else {
+ fprintf(d_fp, "Packet from %s for %s %s with id %d\n", dq->remote->toStringWithPort().c_str(), dq->qname->toString().c_str(), QType(dq->qtype).getName().c_str(), dq->dh->id);
+ }
+ }
+ return Action::None;
+ }
+ string toString() const override
+ {
+ if (!d_fname.empty()) {
+ return "log to " + d_fname;
+ }
+ return "log";
+ }
+private:
+ string d_fname;
+ FILE* d_fp{0};
+ bool d_binary{true};
+};
+
+
+class DisableValidationAction : public DNSAction
+{
+public:
+ DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
+ {
+ dq->dh->cd = true;
+ return Action::None;
+ }
+ string toString() const override
+ {
+ return "set cd=1";
+ }
+};
+
+class SkipCacheAction : public DNSAction
+{
+public:
+ DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
+ {
+ dq->skipCache = true;
+ return Action::None;
+ }
+ string toString() const override
+ {
+ return "skip cache";
+ }
+};
+
+class ECSPrefixLengthAction : public DNSAction
+{
+public:
+ ECSPrefixLengthAction(uint16_t v4Length, uint16_t v6Length) : d_v4PrefixLength(v4Length), d_v6PrefixLength(v6Length)
+ {
+ }
+ DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
+ {
+ dq->ecsPrefixLength = dq->remote->sin4.sin_family == AF_INET ? d_v4PrefixLength : d_v6PrefixLength;
+ return Action::None;
+ }
+ string toString() const override
+ {
+ return "set ECS prefix length to " + std::to_string(d_v4PrefixLength) + "/" + std::to_string(d_v6PrefixLength);
+ }
+private:
+ uint16_t d_v4PrefixLength;
+ uint16_t d_v6PrefixLength;
+};
+
+class ECSOverrideAction : public DNSAction
+{
+public:
+ ECSOverrideAction(bool ecsOverride) : d_ecsOverride(ecsOverride)
+ {
+ }
+ DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
+ {
+ dq->ecsOverride = d_ecsOverride;
+ return Action::None;
+ }
+ string toString() const override
+ {
+ return "set ECS override to " + std::to_string(d_ecsOverride);
+ }
+private:
+ bool d_ecsOverride;
+};
+
+
+class DisableECSAction : public DNSAction
+{
+public:
+ DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
+ {
+ dq->useECS = false;
+ return Action::None;
+ }
+ string toString() const override
+ {
+ return "disable ECS";
+ }
+};
+
+class RemoteLogAction : public DNSAction, public boost::noncopyable
+{
+public:
+ RemoteLogAction(std::shared_ptr<RemoteLogger> logger, boost::optional<std::function<void(const DNSQuestion&, DNSDistProtoBufMessage*)> > alterFunc): d_logger(logger), d_alterFunc(alterFunc)
+ {
+ }
+ DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
+ {
+#ifdef HAVE_PROTOBUF
+ if (!dq->uniqueId) {
+ dq->uniqueId = t_uuidGenerator();
+ }
+
+ DNSDistProtoBufMessage message(*dq);
+ {
+ if (d_alterFunc) {
+ std::lock_guard<std::mutex> lock(g_luamutex);
+ (*d_alterFunc)(*dq, &message);
+ }
+ }
+ std::string data;
+ message.serialize(data);
+ d_logger->queueData(data);
+#endif /* HAVE_PROTOBUF */
+ return Action::None;
+ }
+ string toString() const override
+ {
+ return "remote log to " + (d_logger ? d_logger->toString() : "");
+ }
+private:
+ std::shared_ptr<RemoteLogger> d_logger;
+ boost::optional<std::function<void(const DNSQuestion&, DNSDistProtoBufMessage*)> > d_alterFunc;
+};
+
+class SNMPTrapAction : public DNSAction
+{
+public:
+ SNMPTrapAction(const std::string& reason): d_reason(reason)
+ {
+ }
+ DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
+ {
+ if (g_snmpAgent && g_snmpTrapsEnabled) {
+ g_snmpAgent->sendDNSTrap(*dq, d_reason);
+ }
+
+ return Action::None;
+ }
+ string toString() const override
+ {
+ return "send SNMP trap";
+ }
+private:
+ std::string d_reason;
+};
+
+class TagAction : public DNSAction
+{
+public:
+ TagAction(const std::string tag, std::string value): d_tag(tag), d_value(value)
+ {
+ }
+ DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
+ {
+ if (dq->qTag == nullptr) {
+ dq->qTag = std::make_shared<QTag>();
+ }
+
+ dq->qTag->add(d_tag, d_value);
+
+ return Action::None;
+ }
+ string toString() const override
+ {
+ return "set tag '" + d_tag + "' to value '" + d_value + "'";
+ }
+private:
+ std::string d_tag;
+ std::string d_value;
+};
+
+class RemoteLogResponseAction : public DNSResponseAction, public boost::noncopyable
+{
+public:
+ RemoteLogResponseAction(std::shared_ptr<RemoteLogger> logger, boost::optional<std::function<void(const DNSResponse&, DNSDistProtoBufMessage*)> > alterFunc, bool includeCNAME): d_logger(logger), d_alterFunc(alterFunc), d_includeCNAME(includeCNAME)
+ {
+ }
+ DNSResponseAction::Action operator()(DNSResponse* dr, string* ruleresult) const override
+ {
+#ifdef HAVE_PROTOBUF
+ if (!dr->uniqueId) {
+ dr->uniqueId = t_uuidGenerator();
+ }
+
+ DNSDistProtoBufMessage message(*dr, d_includeCNAME);
+ {
+ if (d_alterFunc) {
+ std::lock_guard<std::mutex> lock(g_luamutex);
+ (*d_alterFunc)(*dr, &message);
+ }
+ }
+ std::string data;
+ message.serialize(data);
+ d_logger->queueData(data);
+#endif /* HAVE_PROTOBUF */
+ return Action::None;
+ }
+ string toString() const override
+ {
+ return "remote log response to " + (d_logger ? d_logger->toString() : "");
+ }
+private:
+ std::shared_ptr<RemoteLogger> d_logger;
+ boost::optional<std::function<void(const DNSResponse&, DNSDistProtoBufMessage*)> > d_alterFunc;
+ bool d_includeCNAME;
+};
+
+class DropResponseAction : public DNSResponseAction
+{
+public:
+ DNSResponseAction::Action operator()(DNSResponse* dr, string* ruleresult) const override
+ {
+ return Action::Drop;
+ }
+ string toString() const override
+ {
+ return "drop";
+ }
+};
+
+class AllowResponseAction : public DNSResponseAction
+{
+public:
+ DNSResponseAction::Action operator()(DNSResponse* dr, string* ruleresult) const override
+ {
+ return Action::Allow;
+ }
+ string toString() const override
+ {
+ return "allow";
+ }
+};
+
+class DelayResponseAction : public DNSResponseAction
+{
+public:
+ DelayResponseAction(int msec) : d_msec(msec)
+ {}
+ DNSResponseAction::Action operator()(DNSResponse* dr, string* ruleresult) const override
+ {
+ *ruleresult=std::to_string(d_msec);
+ return Action::Delay;
+ }
+ string toString() const override
+ {
+ return "delay by "+std::to_string(d_msec)+ " msec";
+ }
+private:
+ int d_msec;
+};
+
+class SNMPTrapResponseAction : public DNSResponseAction
+{
+public:
+ SNMPTrapResponseAction(const std::string& reason): d_reason(reason)
+ {
+ }
+ DNSResponseAction::Action operator()(DNSResponse* dr, string* ruleresult) const override
+ {
+ if (g_snmpAgent && g_snmpTrapsEnabled) {
+ g_snmpAgent->sendDNSTrap(*dr, d_reason);
+ }
+
+ return Action::None;
+ }
+ string toString() const override
+ {
+ return "send SNMP trap";
+ }
+private:
+ std::string d_reason;
+};
+
+class TagResponseAction : public DNSResponseAction
+{
+public:
+ TagResponseAction(const std::string tag, std::string value): d_tag(tag), d_value(value)
+ {
+ }
+ DNSResponseAction::Action operator()(DNSResponse* dr, string* ruleresult) const override
+ {
+ if (dr->qTag == nullptr) {
+ dr->qTag = std::make_shared<QTag>();
+ }
+
+ dr->qTag->add(d_tag, d_value);
+
+ return Action::None;
+ }
+ string toString() const override
+ {
+ return "set tag '" + d_tag + "' to value '" + d_value + "'";
+ }
+private:
+ std::string d_tag;
+ std::string d_value;
+};
+
+void setupLuaActions()
+{
+ g_lua.writeFunction("newRuleAction", [](luadnsrule_t dnsrule, std::shared_ptr<DNSAction> action) {
+ auto rule=makeRule(dnsrule);
+ return std::make_shared<std::pair< luadnsrule_t, std::shared_ptr<DNSAction> > >(rule, action);
+ });
+
+ g_lua.writeFunction("addAction", [](luadnsrule_t var, boost::variant<std::shared_ptr<DNSAction>, std::shared_ptr<DNSResponseAction> > era) {
+ if (era.type() == typeid(std::shared_ptr<DNSResponseAction>)) {
+ throw std::runtime_error("addAction() can only be called with query-related actions, not response-related ones. Are you looking for addResponseAction()?");
+ }
+
+ auto ea = *boost::get<std::shared_ptr<DNSAction>>(&era);
+ setLuaSideEffect();
+ auto rule=makeRule(var);
+ g_rulactions.modify([rule, ea](decltype(g_rulactions)::value_type& rulactions){
+ rulactions.push_back({rule, ea});
+ });
+ });
+
+ g_lua.writeFunction("addLuaAction", [](luadnsrule_t var, LuaAction::func_t func)
+ {
+ setLuaSideEffect();
+ auto rule=makeRule(var);
+ g_rulactions.modify([rule,func](decltype(g_rulactions)::value_type& rulactions){
+ rulactions.push_back({rule,
+ std::make_shared<LuaAction>(func)});
+ });
+ });
+
+ g_lua.writeFunction("addLuaResponseAction", [](luadnsrule_t var, LuaResponseAction::func_t func) {
+ setLuaSideEffect();
+ auto rule=makeRule(var);
+ g_resprulactions.modify([rule,func](decltype(g_resprulactions)::value_type& rulactions){
+ rulactions.push_back({rule,
+ std::make_shared<LuaResponseAction>(func)});
+ });
+ });
+
+ g_lua.writeFunction("addResponseAction", [](luadnsrule_t var, boost::variant<std::shared_ptr<DNSAction>, std::shared_ptr<DNSResponseAction> > era) {
+ if (era.type() == typeid(std::shared_ptr<DNSAction>)) {
+ throw std::runtime_error("addResponseAction() can only be called with response-related actions, not query-related ones. Are you looking for addAction()?");
+ }
+
+ auto ea = *boost::get<std::shared_ptr<DNSResponseAction>>(&era);
+
+ setLuaSideEffect();
+ auto rule=makeRule(var);
+ g_resprulactions.modify([rule, ea](decltype(g_resprulactions)::value_type& rulactions){
+ rulactions.push_back({rule, ea});
+ });
+ });
+
+ g_lua.writeFunction("addCacheHitResponseAction", [](luadnsrule_t var, std::shared_ptr<DNSResponseAction> ea) {
+ setLuaSideEffect();
+ auto rule=makeRule(var);
+ g_cachehitresprulactions.modify([rule, ea](decltype(g_cachehitresprulactions)::value_type& rulactions){
+ rulactions.push_back({rule, ea});
+ });
+ });
+
+ g_lua.registerFunction<void(DNSAction::*)()>("printStats", [](const DNSAction& ta) {
+ setLuaNoSideEffect();
+ auto stats = ta.getStats();
+ for(const auto& s : stats) {
+ g_outputBuffer+=s.first+"\t";
+ if((uint64_t)s.second == s.second)
+ g_outputBuffer += std::to_string((uint64_t)s.second)+"\n";
+ else
+ g_outputBuffer += std::to_string(s.second)+"\n";
+ }
+ });
+
+ g_lua.writeFunction("getAction", [](unsigned int num) {
+ setLuaNoSideEffect();
+ boost::optional<std::shared_ptr<DNSAction>> ret;
+ auto rulactions = g_rulactions.getCopy();
+ if(num < rulactions.size())
+ ret=rulactions[num].second;
+ return ret;
+ });
+
+ g_lua.registerFunction("getStats", &DNSAction::getStats);
+
+ g_lua.writeFunction("LuaAction", [](LuaAction::func_t func) {
+ setLuaSideEffect();
+ return std::shared_ptr<DNSAction>(new LuaAction(func));
+ });
+
+ g_lua.writeFunction("NoRecurseAction", []() {
+ return std::shared_ptr<DNSAction>(new NoRecurseAction);
+ });
+
+ g_lua.writeFunction("MacAddrAction", [](int code) {
+ return std::shared_ptr<DNSAction>(new MacAddrAction(code));
+ });
+
+ g_lua.writeFunction("PoolAction", [](const string& a) {
+ return std::shared_ptr<DNSAction>(new PoolAction(a));
+ });
+
+ g_lua.writeFunction("QPSAction", [](int limit) {
+ return std::shared_ptr<DNSAction>(new QPSAction(limit));
+ });
+
+ g_lua.writeFunction("QPSPoolAction", [](int limit, const string& a) {
+ return std::shared_ptr<DNSAction>(new QPSPoolAction(limit, a));
+ });
+
+ g_lua.writeFunction("SpoofAction", [](boost::variant<string,vector<pair<int, string>>> inp, boost::optional<string> b ) {
+ vector<ComboAddress> addrs;
+ if(auto s = boost::get<string>(&inp))
+ addrs.push_back(ComboAddress(*s));
+ else {
+ const auto& v = boost::get<vector<pair<int,string>>>(inp);
+ for(const auto& a: v)
+ addrs.push_back(ComboAddress(a.second));
+ }
+ if(b)
+ addrs.push_back(ComboAddress(*b));
+ return std::shared_ptr<DNSAction>(new SpoofAction(addrs));
+ });
+
+ g_lua.writeFunction("SpoofCNAMEAction", [](const string& a) {
+ return std::shared_ptr<DNSAction>(new SpoofAction(a));
+ });
+
+ g_lua.writeFunction("DropAction", []() {
+ return std::shared_ptr<DNSAction>(new DropAction);
+ });
+
+ g_lua.writeFunction("AllowAction", []() {
+ return std::shared_ptr<DNSAction>(new AllowAction);
+ });
+
+ g_lua.writeFunction("DelayAction", [](int msec) {
+ return std::shared_ptr<DNSAction>(new DelayAction(msec));
+ });
+
+ g_lua.writeFunction("TCAction", []() {
+ return std::shared_ptr<DNSAction>(new TCAction);
+ });
+
+ g_lua.writeFunction("DisableValidationAction", []() {
+ return std::shared_ptr<DNSAction>(new DisableValidationAction);
+ });
+
+ g_lua.writeFunction("LogAction", [](const std::string& fname, boost::optional<bool> binary, boost::optional<bool> append, boost::optional<bool> buffered) {
+ return std::shared_ptr<DNSAction>(new LogAction(fname, binary ? *binary : true, append ? *append : false, buffered ? *buffered : false));
+ });
+
+ g_lua.writeFunction("RCodeAction", [](int rcode) {
+ return std::shared_ptr<DNSAction>(new RCodeAction(rcode));
+ });
+
+ g_lua.writeFunction("SkipCacheAction", []() {
+ return std::shared_ptr<DNSAction>(new SkipCacheAction);
+ });
+
+ g_lua.writeFunction("DropResponseAction", []() {
+ return std::shared_ptr<DNSResponseAction>(new DropResponseAction);
+ });
+
+ g_lua.writeFunction("AllowResponseAction", []() {
+ return std::shared_ptr<DNSResponseAction>(new AllowResponseAction);
+ });
+
+ g_lua.writeFunction("DelayResponseAction", [](int msec) {
+ return std::shared_ptr<DNSResponseAction>(new DelayResponseAction(msec));
+ });
+
+ g_lua.writeFunction("LuaResponseAction", [](LuaResponseAction::func_t func) {
+ setLuaSideEffect();
+ return std::shared_ptr<DNSResponseAction>(new LuaResponseAction(func));
+ });
+
+ g_lua.writeFunction("RemoteLogAction", [](std::shared_ptr<RemoteLogger> logger, boost::optional<std::function<void(const DNSQuestion&, DNSDistProtoBufMessage*)> > alterFunc) {
+#ifdef HAVE_PROTOBUF
+ return std::shared_ptr<DNSAction>(new RemoteLogAction(logger, alterFunc));
+#else
+ throw std::runtime_error("Protobuf support is required to use RemoteLogAction");
+#endif
+ });
+
+ g_lua.writeFunction("RemoteLogResponseAction", [](std::shared_ptr<RemoteLogger> logger, boost::optional<std::function<void(const DNSResponse&, DNSDistProtoBufMessage*)> > alterFunc, boost::optional<bool> includeCNAME) {
+#ifdef HAVE_PROTOBUF
+ return std::shared_ptr<DNSResponseAction>(new RemoteLogResponseAction(logger, alterFunc, includeCNAME ? *includeCNAME : false));
+#else
+ throw std::runtime_error("Protobuf support is required to use RemoteLogResponseAction");
+#endif
+ });
+
+ g_lua.writeFunction("TeeAction", [](const std::string& remote, boost::optional<bool> addECS) {
+ return std::shared_ptr<DNSAction>(new TeeAction(ComboAddress(remote, 53), addECS ? *addECS : false));
+ });
+
+ g_lua.writeFunction("ECSPrefixLengthAction", [](uint16_t v4PrefixLength, uint16_t v6PrefixLength) {
+ return std::shared_ptr<DNSAction>(new ECSPrefixLengthAction(v4PrefixLength, v6PrefixLength));
+ });
+
+ g_lua.writeFunction("ECSOverrideAction", [](bool ecsOverride) {
+ return std::shared_ptr<DNSAction>(new ECSOverrideAction(ecsOverride));
+ });
+
+ g_lua.writeFunction("DisableECSAction", []() {
+ return std::shared_ptr<DNSAction>(new DisableECSAction());
+ });
+
+ g_lua.writeFunction("SNMPTrapAction", [](boost::optional<std::string> reason) {
+#ifdef HAVE_NET_SNMP
+ return std::shared_ptr<DNSAction>(new SNMPTrapAction(reason ? *reason : ""));
+#else
+ throw std::runtime_error("NET SNMP support is required to use SNMPTrapAction()");
+#endif /* HAVE_NET_SNMP */
+ });
+
+ g_lua.writeFunction("SNMPTrapResponseAction", [](boost::optional<std::string> reason) {
+#ifdef HAVE_NET_SNMP
+ return std::shared_ptr<DNSResponseAction>(new SNMPTrapResponseAction(reason ? *reason : ""));
+#else
+ throw std::runtime_error("NET SNMP support is required to use SNMPTrapResponseAction()");
+#endif /* HAVE_NET_SNMP */
+ });
+
+ g_lua.writeFunction("TagAction", [](std::string tag, std::string value) {
+ return std::shared_ptr<DNSAction>(new TagAction(tag, value));
+ });
+
+ g_lua.writeFunction("TagResponseAction", [](std::string tag, std::string value) {
+ return std::shared_ptr<DNSResponseAction>(new TagResponseAction(tag, value));
+ });
+}
--- /dev/null
+/*
+ * 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-lua.hh"
+#include "dnsparser.hh"
+
+void setupLuaBindingsDNSQuestion()
+{
+ /* DNSQuestion */
+ /* PowerDNS DNSQuestion compat */
+ g_lua.registerMember<const ComboAddress (DNSQuestion::*)>("localaddr", [](const DNSQuestion& dq) -> const ComboAddress { return *dq.local; }, [](DNSQuestion& dq, const ComboAddress newLocal) { (void) newLocal; });
+ g_lua.registerMember<const DNSName (DNSQuestion::*)>("qname", [](const DNSQuestion& dq) -> const DNSName { return *dq.qname; }, [](DNSQuestion& dq, const DNSName newName) { (void) newName; });
+ g_lua.registerMember<uint16_t (DNSQuestion::*)>("qtype", [](const DNSQuestion& dq) -> uint16_t { return dq.qtype; }, [](DNSQuestion& dq, uint16_t newType) { (void) newType; });
+ g_lua.registerMember<uint16_t (DNSQuestion::*)>("qclass", [](const DNSQuestion& dq) -> uint16_t { return dq.qclass; }, [](DNSQuestion& dq, uint16_t newClass) { (void) newClass; });
+ g_lua.registerMember<int (DNSQuestion::*)>("rcode", [](const DNSQuestion& dq) -> int { return dq.dh->rcode; }, [](DNSQuestion& dq, int newRCode) { dq.dh->rcode = newRCode; });
+ g_lua.registerMember<const ComboAddress (DNSQuestion::*)>("remoteaddr", [](const DNSQuestion& dq) -> const ComboAddress { return *dq.remote; }, [](DNSQuestion& dq, const ComboAddress newRemote) { (void) newRemote; });
+ /* DNSDist DNSQuestion */
+ g_lua.registerMember("dh", &DNSQuestion::dh);
+ g_lua.registerMember<uint16_t (DNSQuestion::*)>("len", [](const DNSQuestion& dq) -> uint16_t { return dq.len; }, [](DNSQuestion& dq, uint16_t newlen) { dq.len = newlen; });
+ g_lua.registerMember<uint8_t (DNSQuestion::*)>("opcode", [](const DNSQuestion& dq) -> uint8_t { return dq.dh->opcode; }, [](DNSQuestion& dq, uint8_t newOpcode) { (void) newOpcode; });
+ g_lua.registerMember<size_t (DNSQuestion::*)>("size", [](const DNSQuestion& dq) -> size_t { return dq.size; }, [](DNSQuestion& dq, size_t newSize) { (void) newSize; });
+ g_lua.registerMember<bool (DNSQuestion::*)>("tcp", [](const DNSQuestion& dq) -> bool { return dq.tcp; }, [](DNSQuestion& dq, bool newTcp) { (void) newTcp; });
+ g_lua.registerMember<bool (DNSQuestion::*)>("skipCache", [](const DNSQuestion& dq) -> bool { return dq.skipCache; }, [](DNSQuestion& dq, bool newSkipCache) { dq.skipCache = newSkipCache; });
+ g_lua.registerMember<bool (DNSQuestion::*)>("useECS", [](const DNSQuestion& dq) -> bool { return dq.useECS; }, [](DNSQuestion& dq, bool useECS) { dq.useECS = useECS; });
+ g_lua.registerMember<bool (DNSQuestion::*)>("ecsOverride", [](const DNSQuestion& dq) -> bool { return dq.ecsOverride; }, [](DNSQuestion& dq, bool ecsOverride) { dq.ecsOverride = ecsOverride; });
+ g_lua.registerMember<uint16_t (DNSQuestion::*)>("ecsPrefixLength", [](const DNSQuestion& dq) -> uint16_t { return dq.ecsPrefixLength; }, [](DNSQuestion& dq, uint16_t newPrefixLength) { dq.ecsPrefixLength = newPrefixLength; });
+ g_lua.registerFunction<bool(DNSQuestion::*)()>("getDO", [](const DNSQuestion& dq) {
+ return getEDNSZ((const char*)dq.dh, dq.len) & EDNS_HEADER_FLAG_DO;
+ });
+ g_lua.registerFunction<void(DNSQuestion::*)(std::string)>("sendTrap", [](const DNSQuestion& dq, boost::optional<std::string> reason) {
+#ifdef HAVE_NET_SNMP
+ if (g_snmpAgent && g_snmpTrapsEnabled) {
+ g_snmpAgent->sendDNSTrap(dq, reason ? *reason : "");
+ }
+#endif /* HAVE_NET_SNMP */
+ });
+ g_lua.registerFunction<void(DNSQuestion::*)(std::string, std::string)>("setTag", [](DNSQuestion& dq, const std::string& strLabel, const std::string& strValue) {
+
+ if(dq.qTag == nullptr) {
+ dq.qTag = std::make_shared<QTag>();
+ }
+ dq.qTag->add(strLabel, strValue);
+
+ });
+ g_lua.registerFunction<void(DNSQuestion::*)(vector<pair<string, string>>)>("setTagArray", [](DNSQuestion& dq, const vector<pair<string, string>>&tags) {
+
+ if(dq.qTag == nullptr) {
+ dq.qTag = std::make_shared<QTag>();
+ }
+
+ for (const auto& tag : tags) {
+ dq.qTag->add(tag.first, tag.second);
+ }
+
+ });
+ g_lua.registerFunction<string(DNSQuestion::*)(std::string)>("getTag", [](const DNSQuestion& dq, const std::string& strLabel) {
+
+ std::string strValue;
+ if(dq.qTag != nullptr) {
+ strValue = dq.qTag->getMatch(strLabel);
+ }
+ return strValue;
+ });
+ g_lua.registerFunction<std::unordered_map<string, string>(DNSQuestion::*)(void)>("getTagArray", [](const DNSQuestion& dq) {
+
+ if(dq.qTag != nullptr) {
+ return dq.qTag->tagData;
+ } else {
+ std::unordered_map<string, string> XX;
+ return XX;
+ }
+ });
+
+ /* LuaWrapper doesn't support inheritance */
+ g_lua.registerMember<const ComboAddress (DNSResponse::*)>("localaddr", [](const DNSResponse& dq) -> const ComboAddress { return *dq.local; }, [](DNSResponse& dq, const ComboAddress newLocal) { (void) newLocal; });
+ g_lua.registerMember<const DNSName (DNSResponse::*)>("qname", [](const DNSResponse& dq) -> const DNSName { return *dq.qname; }, [](DNSResponse& dq, const DNSName newName) { (void) newName; });
+ g_lua.registerMember<uint16_t (DNSResponse::*)>("qtype", [](const DNSResponse& dq) -> uint16_t { return dq.qtype; }, [](DNSResponse& dq, uint16_t newType) { (void) newType; });
+ g_lua.registerMember<uint16_t (DNSResponse::*)>("qclass", [](const DNSResponse& dq) -> uint16_t { return dq.qclass; }, [](DNSResponse& dq, uint16_t newClass) { (void) newClass; });
+ g_lua.registerMember<int (DNSResponse::*)>("rcode", [](const DNSResponse& dq) -> int { return dq.dh->rcode; }, [](DNSResponse& dq, int newRCode) { dq.dh->rcode = newRCode; });
+ g_lua.registerMember<const ComboAddress (DNSResponse::*)>("remoteaddr", [](const DNSResponse& dq) -> const ComboAddress { return *dq.remote; }, [](DNSResponse& dq, const ComboAddress newRemote) { (void) newRemote; });
+ g_lua.registerMember<dnsheader* (DNSResponse::*)>("dh", [](const DNSResponse& dr) -> dnsheader* { return dr.dh; }, [](DNSResponse& dr, dnsheader * newdh) { dr.dh = newdh; });
+ g_lua.registerMember<uint16_t (DNSResponse::*)>("len", [](const DNSResponse& dq) -> uint16_t { return dq.len; }, [](DNSResponse& dq, uint16_t newlen) { dq.len = newlen; });
+ g_lua.registerMember<uint8_t (DNSResponse::*)>("opcode", [](const DNSResponse& dq) -> uint8_t { return dq.dh->opcode; }, [](DNSResponse& dq, uint8_t newOpcode) { (void) newOpcode; });
+ g_lua.registerMember<size_t (DNSResponse::*)>("size", [](const DNSResponse& dq) -> size_t { return dq.size; }, [](DNSResponse& dq, size_t newSize) { (void) newSize; });
+ g_lua.registerMember<bool (DNSResponse::*)>("tcp", [](const DNSResponse& dq) -> bool { return dq.tcp; }, [](DNSResponse& dq, bool newTcp) { (void) newTcp; });
+ g_lua.registerMember<bool (DNSResponse::*)>("skipCache", [](const DNSResponse& dq) -> bool { return dq.skipCache; }, [](DNSResponse& dq, bool newSkipCache) { dq.skipCache = newSkipCache; });
+ g_lua.registerFunction<void(DNSResponse::*)(std::function<uint32_t(uint8_t section, uint16_t qclass, uint16_t qtype, uint32_t ttl)> editFunc)>("editTTLs", [](const DNSResponse& dr, std::function<uint32_t(uint8_t section, uint16_t qclass, uint16_t qtype, uint32_t ttl)> editFunc) {
+ editDNSPacketTTL((char*) dr.dh, dr.len, editFunc);
+ });
+ g_lua.registerFunction<void(DNSResponse::*)(std::string)>("sendTrap", [](const DNSResponse& dr, boost::optional<std::string> reason) {
+#ifdef HAVE_NET_SNMP
+ if (g_snmpAgent && g_snmpTrapsEnabled) {
+ g_snmpAgent->sendDNSTrap(dr, reason ? *reason : "");
+ }
+#endif /* HAVE_NET_SNMP */
+ });
+}
--- /dev/null
+/*
+ * 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-lua.hh"
+#include "dnsdist-protobuf.hh"
+
+#include "dolog.hh"
+#include "remote_logger.hh"
+
+void setupLuaBindings(bool client)
+{
+ g_lua.writeFunction("infolog", [](const string& arg) {
+ infolog("%s", arg);
+ });
+ g_lua.writeFunction("errlog", [](const string& arg) {
+ errlog("%s", arg);
+ });
+ g_lua.writeFunction("warnlog", [](const string& arg) {
+ warnlog("%s", arg);
+ });
+ g_lua.writeFunction("show", [](const string& arg) {
+ g_outputBuffer+=arg;
+ g_outputBuffer+="\n";
+ });
+
+ /* ServerPolicy */
+ g_lua.writeFunction("newServerPolicy", [](string name, policyfunc_t policy) { return ServerPolicy{name, policy};});
+ g_lua.registerMember("name", &ServerPolicy::name);
+ g_lua.registerMember("policy", &ServerPolicy::policy);
+
+ g_lua.writeVariable("firstAvailable", ServerPolicy{"firstAvailable", firstAvailable});
+ g_lua.writeVariable("roundrobin", ServerPolicy{"roundrobin", roundrobin});
+ g_lua.writeVariable("wrandom", ServerPolicy{"wrandom", wrandom});
+ g_lua.writeVariable("whashed", ServerPolicy{"whashed", whashed});
+ g_lua.writeVariable("leastOutstanding", ServerPolicy{"leastOutstanding", leastOutstanding});
+
+ /* ServerPool */
+ g_lua.registerFunction<void(std::shared_ptr<ServerPool>::*)(std::shared_ptr<DNSDistPacketCache>)>("setCache", [](std::shared_ptr<ServerPool> pool, std::shared_ptr<DNSDistPacketCache> cache) {
+ if (pool) {
+ pool->packetCache = cache;
+ }
+ });
+ g_lua.registerFunction("getCache", &ServerPool::getCache);
+ g_lua.registerFunction<void(std::shared_ptr<ServerPool>::*)()>("unsetCache", [](std::shared_ptr<ServerPool> pool) {
+ if (pool) {
+ pool->packetCache = nullptr;
+ }
+ });
+
+ /* DownstreamState */
+ g_lua.registerFunction<void(DownstreamState::*)(int)>("setQPS", [](DownstreamState& s, int lim) { s.qps = lim ? QPSLimiter(lim, lim) : QPSLimiter(); });
+ g_lua.registerFunction<void(std::shared_ptr<DownstreamState>::*)(string)>("addPool", [](std::shared_ptr<DownstreamState> s, string pool) {
+ auto localPools = g_pools.getCopy();
+ addServerToPool(localPools, pool, s);
+ g_pools.setState(localPools);
+ s->pools.insert(pool);
+ });
+ g_lua.registerFunction<void(std::shared_ptr<DownstreamState>::*)(string)>("rmPool", [](std::shared_ptr<DownstreamState> s, string pool) {
+ auto localPools = g_pools.getCopy();
+ removeServerFromPool(localPools, pool, s);
+ g_pools.setState(localPools);
+ s->pools.erase(pool);
+ });
+ g_lua.registerFunction<void(DownstreamState::*)()>("getOutstanding", [](const DownstreamState& s) { g_outputBuffer=std::to_string(s.outstanding.load()); });
+ g_lua.registerFunction("isUp", &DownstreamState::isUp);
+ g_lua.registerFunction("setDown", &DownstreamState::setDown);
+ g_lua.registerFunction("setUp", &DownstreamState::setUp);
+ g_lua.registerFunction<void(DownstreamState::*)(boost::optional<bool> newStatus)>("setAuto", [](DownstreamState& s, boost::optional<bool> newStatus) {
+ if (newStatus) {
+ s.upStatus = *newStatus;
+ }
+ s.setAuto();
+ });
+ g_lua.registerFunction("getName", &DownstreamState::getName);
+ g_lua.registerFunction("getNameWithAddr", &DownstreamState::getNameWithAddr);
+ g_lua.registerMember("upStatus", &DownstreamState::upStatus);
+ g_lua.registerMember("weight", &DownstreamState::weight);
+ g_lua.registerMember("order", &DownstreamState::order);
+ g_lua.registerMember("name", &DownstreamState::name);
+
+ /* dnsheader */
+ g_lua.registerFunction<void(dnsheader::*)(bool)>("setRD", [](dnsheader& dh, bool v) {
+ dh.rd=v;
+ });
+
+ g_lua.registerFunction<bool(dnsheader::*)()>("getRD", [](dnsheader& dh) {
+ return (bool)dh.rd;
+ });
+
+ g_lua.registerFunction<void(dnsheader::*)(bool)>("setCD", [](dnsheader& dh, bool v) {
+ dh.cd=v;
+ });
+
+ g_lua.registerFunction<bool(dnsheader::*)()>("getCD", [](dnsheader& dh) {
+ return (bool)dh.cd;
+ });
+
+ g_lua.registerFunction<void(dnsheader::*)(bool)>("setTC", [](dnsheader& dh, bool v) {
+ dh.tc=v;
+ if(v) dh.ra = dh.rd; // you'll always need this, otherwise TC=1 gets ignored
+ });
+
+ g_lua.registerFunction<void(dnsheader::*)(bool)>("setQR", [](dnsheader& dh, bool v) {
+ dh.qr=v;
+ });
+
+ /* ComboAddress */
+ g_lua.writeFunction("newCA", [](const std::string& name) { return ComboAddress(name); });
+ g_lua.registerFunction<string(ComboAddress::*)()>("tostring", [](const ComboAddress& ca) { return ca.toString(); });
+ g_lua.registerFunction<string(ComboAddress::*)()>("tostringWithPort", [](const ComboAddress& ca) { return ca.toStringWithPort(); });
+ g_lua.registerFunction<string(ComboAddress::*)()>("toString", [](const ComboAddress& ca) { return ca.toString(); });
+ g_lua.registerFunction<string(ComboAddress::*)()>("toStringWithPort", [](const ComboAddress& ca) { return ca.toStringWithPort(); });
+ g_lua.registerFunction<uint16_t(ComboAddress::*)()>("getPort", [](const ComboAddress& ca) { return ntohs(ca.sin4.sin_port); } );
+ g_lua.registerFunction<void(ComboAddress::*)(unsigned int)>("truncate", [](ComboAddress& ca, unsigned int bits) { ca.truncate(bits); });
+ g_lua.registerFunction<bool(ComboAddress::*)()>("isIPv4", [](const ComboAddress& ca) { return ca.sin4.sin_family == AF_INET; });
+ g_lua.registerFunction<bool(ComboAddress::*)()>("isIPv6", [](const ComboAddress& ca) { return ca.sin4.sin_family == AF_INET6; });
+ g_lua.registerFunction<bool(ComboAddress::*)()>("isMappedIPv4", [](const ComboAddress& ca) { return ca.isMappedIPv4(); });
+ g_lua.registerFunction<ComboAddress(ComboAddress::*)()>("mapToIPv4", [](const ComboAddress& ca) { return ca.mapToIPv4(); });
+ g_lua.registerFunction<bool(nmts_t::*)(const ComboAddress&)>("match", [](nmts_t& s, const ComboAddress& ca) { return s.match(ca); });
+
+ /* DNSName */
+ g_lua.registerFunction("isPartOf", &DNSName::isPartOf);
+ g_lua.registerFunction<bool(DNSName::*)()>("chopOff", [](DNSName&dn ) { return dn.chopOff(); });
+ g_lua.registerFunction<unsigned int(DNSName::*)()>("countLabels", [](const DNSName& name) { return name.countLabels(); });
+ g_lua.registerFunction<size_t(DNSName::*)()>("wirelength", [](const DNSName& name) { return name.wirelength(); });
+ g_lua.registerFunction<string(DNSName::*)()>("tostring", [](const DNSName&dn ) { return dn.toString(); });
+ g_lua.registerFunction<string(DNSName::*)()>("toString", [](const DNSName&dn ) { return dn.toString(); });
+ g_lua.writeFunction("newDNSName", [](const std::string& name) { return DNSName(name); });
+ g_lua.writeFunction("newSuffixMatchNode", []() { return SuffixMatchNode(); });
+
+ /* SuffixMatchNode */
+ g_lua.registerFunction("add",(void (SuffixMatchNode::*)(const DNSName&)) &SuffixMatchNode::add);
+ g_lua.registerFunction("check",(bool (SuffixMatchNode::*)(const DNSName&) const) &SuffixMatchNode::check);
+
+ /* NetmaskGroup */
+ g_lua.writeFunction("newNMG", []() { return NetmaskGroup(); });
+ g_lua.registerFunction<void(NetmaskGroup::*)(const std::string&mask)>("addMask", [](NetmaskGroup&nmg, const std::string& mask)
+ {
+ nmg.addMask(mask);
+ });
+ g_lua.registerFunction<void(NetmaskGroup::*)(const std::map<ComboAddress,int>& map)>("addMasks", [](NetmaskGroup&nmg, const std::map<ComboAddress,int>& map)
+ {
+ for (const auto& entry : map) {
+ nmg.addMask(Netmask(entry.first));
+ }
+ });
+
+ g_lua.registerFunction("match", (bool (NetmaskGroup::*)(const ComboAddress&) const)&NetmaskGroup::match);
+ g_lua.registerFunction("size", &NetmaskGroup::size);
+ g_lua.registerFunction("clear", &NetmaskGroup::clear);
+
+ /* QPSLimiter */
+ g_lua.writeFunction("newQPSLimiter", [](int rate, int burst) { return QPSLimiter(rate, burst); });
+ g_lua.registerFunction("check", &QPSLimiter::check);
+
+ /* ClientState */
+ g_lua.registerFunction<std::string(ClientState::*)()>("toString", [](const ClientState& fe) {
+ setLuaNoSideEffect();
+ return fe.local.toStringWithPort();
+ });
+ g_lua.registerMember("muted", &ClientState::muted);
+#ifdef HAVE_EBPF
+ g_lua.registerFunction<void(ClientState::*)(std::shared_ptr<BPFFilter>)>("attachFilter", [](ClientState& frontend, std::shared_ptr<BPFFilter> bpf) {
+ if (bpf) {
+ frontend.attachFilter(bpf);
+ }
+ });
+ g_lua.registerFunction<void(ClientState::*)()>("detachFilter", [](ClientState& frontend) {
+ frontend.detachFilter();
+ });
+#endif /* HAVE_EBPF */
+
+ /* PacketCache */
+ g_lua.writeFunction("newPacketCache", [](size_t maxEntries, boost::optional<uint32_t> maxTTL, boost::optional<uint32_t> minTTL, boost::optional<uint32_t> tempFailTTL, boost::optional<uint32_t> staleTTL, boost::optional<bool> dontAge, boost::optional<size_t> numberOfShards, boost::optional<bool> deferrableInsertLock) {
+ return std::make_shared<DNSDistPacketCache>(maxEntries, maxTTL ? *maxTTL : 86400, minTTL ? *minTTL : 0, tempFailTTL ? *tempFailTTL : 60, staleTTL ? *staleTTL : 60, dontAge ? *dontAge : false, numberOfShards ? *numberOfShards : 1, deferrableInsertLock ? *deferrableInsertLock : true);
+ });
+ g_lua.registerFunction("toString", &DNSDistPacketCache::toString);
+ g_lua.registerFunction("isFull", &DNSDistPacketCache::isFull);
+ g_lua.registerFunction("purgeExpired", &DNSDistPacketCache::purgeExpired);
+ g_lua.registerFunction("expunge", &DNSDistPacketCache::expunge);
+ g_lua.registerFunction<void(std::shared_ptr<DNSDistPacketCache>::*)(const DNSName& dname, boost::optional<uint16_t> qtype, boost::optional<bool> suffixMatch)>("expungeByName", [](
+ std::shared_ptr<DNSDistPacketCache> cache,
+ const DNSName& dname,
+ boost::optional<uint16_t> qtype,
+ boost::optional<bool> suffixMatch) {
+ if (cache) {
+ cache->expungeByName(dname, qtype ? *qtype : QType::ANY, suffixMatch ? *suffixMatch : false);
+ }
+ });
+ g_lua.registerFunction<void(std::shared_ptr<DNSDistPacketCache>::*)()>("printStats", [](const std::shared_ptr<DNSDistPacketCache> cache) {
+ if (cache) {
+ g_outputBuffer="Entries: " + std::to_string(cache->getEntriesCount()) + "/" + std::to_string(cache->getMaxEntries()) + "\n";
+ g_outputBuffer+="Hits: " + std::to_string(cache->getHits()) + "\n";
+ g_outputBuffer+="Misses: " + std::to_string(cache->getMisses()) + "\n";
+ g_outputBuffer+="Deferred inserts: " + std::to_string(cache->getDeferredInserts()) + "\n";
+ g_outputBuffer+="Deferred lookups: " + std::to_string(cache->getDeferredLookups()) + "\n";
+ g_outputBuffer+="Lookup Collisions: " + std::to_string(cache->getLookupCollisions()) + "\n";
+ g_outputBuffer+="Insert Collisions: " + std::to_string(cache->getInsertCollisions()) + "\n";
+ g_outputBuffer+="TTL Too Shorts: " + std::to_string(cache->getTTLTooShorts()) + "\n";
+ }
+ });
+
+ /* ProtobufMessage */
+ g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(std::string)>("setTag", [](DNSDistProtoBufMessage& message, const std::string& strValue) {
+ message.addTag(strValue);
+ });
+ g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(vector<pair<int, string>>)>("setTagArray", [](DNSDistProtoBufMessage& message, const vector<pair<int, string>>&tags) {
+ for (const auto& tag : tags) {
+ message.addTag(tag.second);
+ }
+ });
+ g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(boost::optional <time_t> sec, boost::optional <uint32_t> uSec)>("setProtobufResponseType",
+ [](DNSDistProtoBufMessage& message, boost::optional <time_t> sec, boost::optional <uint32_t> uSec) {
+ message.setType(DNSProtoBufMessage::Response);
+ message.setQueryTime(sec?*sec:0, uSec?*uSec:0);
+ });
+ g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(const std::string& strQueryName, uint16_t uType, uint16_t uClass, uint32_t uTTL, const std::string& strBlob)>("addResponseRR", [](DNSDistProtoBufMessage& message,
+ const std::string& strQueryName, uint16_t uType, uint16_t uClass, uint32_t uTTL, const std::string& strBlob) {
+ message.addRR(DNSName(strQueryName), uType, uClass, uTTL, strBlob);
+ });
+ g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(const Netmask&)>("setEDNSSubnet", [](DNSDistProtoBufMessage& message, const Netmask& subnet) { message.setEDNSSubnet(subnet); });
+ g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(const DNSName&, uint16_t, uint16_t)>("setQuestion", [](DNSDistProtoBufMessage& message, const DNSName& qname, uint16_t qtype, uint16_t qclass) { message.setQuestion(qname, qtype, qclass); });
+ g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(size_t)>("setBytes", [](DNSDistProtoBufMessage& message, size_t bytes) { message.setBytes(bytes); });
+ g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(time_t, uint32_t)>("setTime", [](DNSDistProtoBufMessage& message, time_t sec, uint32_t usec) { message.setTime(sec, usec); });
+ g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(time_t, uint32_t)>("setQueryTime", [](DNSDistProtoBufMessage& message, time_t sec, uint32_t usec) { message.setQueryTime(sec, usec); });
+ g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(uint8_t)>("setResponseCode", [](DNSDistProtoBufMessage& message, uint8_t rcode) { message.setResponseCode(rcode); });
+ g_lua.registerFunction<std::string(DNSDistProtoBufMessage::*)()>("toDebugString", [](const DNSDistProtoBufMessage& message) { return message.toDebugString(); });
+ g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(const ComboAddress&)>("setRequestor", [](DNSDistProtoBufMessage& message, const ComboAddress& addr) {
+ message.setRequestor(addr);
+ });
+ g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(const std::string&)>("setRequestorFromString", [](DNSDistProtoBufMessage& message, const std::string& str) {
+ message.setRequestor(str);
+ });
+ g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(const ComboAddress&)>("setResponder", [](DNSDistProtoBufMessage& message, const ComboAddress& addr) {
+ message.setResponder(addr);
+ });
+ g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(const std::string&)>("setResponderFromString", [](DNSDistProtoBufMessage& message, const std::string& str) {
+ message.setResponder(str);
+ });
+
+ /* RemoteLogger */
+ g_lua.writeFunction("newRemoteLogger", [client](const std::string& remote, boost::optional<uint16_t> timeout, boost::optional<uint64_t> maxQueuedEntries, boost::optional<uint8_t> reconnectWaitTime) {
+ if (client) {
+ return std::shared_ptr<RemoteLogger>();
+ }
+ return std::make_shared<RemoteLogger>(ComboAddress(remote), timeout ? *timeout : 2, maxQueuedEntries ? *maxQueuedEntries : 100, reconnectWaitTime ? *reconnectWaitTime : 1);
+ });
+
+#ifdef HAVE_DNSCRYPT
+ /* DnsCryptContext bindings */
+ g_lua.registerFunction<std::string(DnsCryptContext::*)()>("getProviderName", [](const DnsCryptContext& ctx) { return ctx.getProviderName(); });
+ g_lua.registerFunction<DnsCryptCert(DnsCryptContext::*)()>("getCurrentCertificate", [](const DnsCryptContext& ctx) { return ctx.getCurrentCertificate(); });
+ g_lua.registerFunction<DnsCryptCert(DnsCryptContext::*)()>("getOldCertificate", [](const DnsCryptContext& ctx) { return ctx.getOldCertificate(); });
+ g_lua.registerFunction("hasOldCertificate", &DnsCryptContext::hasOldCertificate);
+ g_lua.registerFunction("loadNewCertificate", &DnsCryptContext::loadNewCertificate);
+ g_lua.registerFunction<void(DnsCryptContext::*)(const std::string& providerPrivateKeyFile, uint32_t serial, time_t begin, time_t end)>("generateAndLoadInMemoryCertificate", [](DnsCryptContext& ctx, const std::string& providerPrivateKeyFile, uint32_t serial, time_t begin, time_t end) {
+ DnsCryptPrivateKey privateKey;
+ DnsCryptCert cert;
+
+ try {
+ if (generateDNSCryptCertificate(providerPrivateKeyFile, serial, begin, end, cert, privateKey)) {
+ ctx.setNewCertificate(cert, privateKey);
+ }
+ }
+ catch(const std::exception& e) {
+ errlog(e.what());
+ g_outputBuffer="Error: "+string(e.what())+"\n";
+ }
+ });
+
+ /* DnsCryptCert */
+ g_lua.registerFunction<std::string(DnsCryptCert::*)()>("getMagic", [](const DnsCryptCert& cert) { return std::string(reinterpret_cast<const char*>(cert.magic), sizeof(cert.magic)); });
+ g_lua.registerFunction<std::string(DnsCryptCert::*)()>("getEsVersion", [](const DnsCryptCert& cert) { return std::string(reinterpret_cast<const char*>(cert.esVersion), sizeof(cert.esVersion)); });
+ g_lua.registerFunction<std::string(DnsCryptCert::*)()>("getProtocolMinorVersion", [](const DnsCryptCert& cert) { return std::string(reinterpret_cast<const char*>(cert.protocolMinorVersion), sizeof(cert.protocolMinorVersion)); });
+ g_lua.registerFunction<std::string(DnsCryptCert::*)()>("getSignature", [](const DnsCryptCert& cert) { return std::string(reinterpret_cast<const char*>(cert.signature), sizeof(cert.signature)); });
+ g_lua.registerFunction<std::string(DnsCryptCert::*)()>("getResolverPublicKey", [](const DnsCryptCert& cert) { return std::string(reinterpret_cast<const char*>(cert.signedData.resolverPK), sizeof(cert.signedData.resolverPK)); });
+ g_lua.registerFunction<std::string(DnsCryptCert::*)()>("getClientMagic", [](const DnsCryptCert& cert) { return std::string(reinterpret_cast<const char*>(cert.signedData.clientMagic), sizeof(cert.signedData.clientMagic)); });
+ g_lua.registerFunction<uint32_t(DnsCryptCert::*)()>("getSerial", [](const DnsCryptCert& cert) { return cert.signedData.serial; });
+ g_lua.registerFunction<uint32_t(DnsCryptCert::*)()>("getTSStart", [](const DnsCryptCert& cert) { return ntohl(cert.signedData.tsStart); });
+ g_lua.registerFunction<uint32_t(DnsCryptCert::*)()>("getTSEnd", [](const DnsCryptCert& cert) { return ntohl(cert.signedData.tsEnd); });
+#endif
+
+ /* BPF Filter */
+#ifdef HAVE_EBPF
+ g_lua.writeFunction("newBPFFilter", [client](uint32_t maxV4, uint32_t maxV6, uint32_t maxQNames) {
+ if (client) {
+ return std::shared_ptr<BPFFilter>(nullptr);
+ }
+ return std::make_shared<BPFFilter>(maxV4, maxV6, maxQNames);
+ });
+
+ g_lua.registerFunction<void(std::shared_ptr<BPFFilter>::*)(const ComboAddress& ca)>("block", [](std::shared_ptr<BPFFilter> bpf, const ComboAddress& ca) {
+ if (bpf) {
+ return bpf->block(ca);
+ }
+ });
+
+ g_lua.registerFunction<void(std::shared_ptr<BPFFilter>::*)(const DNSName& qname, boost::optional<uint16_t> qtype)>("blockQName", [](std::shared_ptr<BPFFilter> bpf, const DNSName& qname, boost::optional<uint16_t> qtype) {
+ if (bpf) {
+ return bpf->block(qname, qtype ? *qtype : 255);
+ }
+ });
+
+ g_lua.registerFunction<void(std::shared_ptr<BPFFilter>::*)(const ComboAddress& ca)>("unblock", [](std::shared_ptr<BPFFilter> bpf, const ComboAddress& ca) {
+ if (bpf) {
+ return bpf->unblock(ca);
+ }
+ });
+
+ g_lua.registerFunction<void(std::shared_ptr<BPFFilter>::*)(const DNSName& qname, boost::optional<uint16_t> qtype)>("unblockQName", [](std::shared_ptr<BPFFilter> bpf, const DNSName& qname, boost::optional<uint16_t> qtype) {
+ if (bpf) {
+ return bpf->unblock(qname, qtype ? *qtype : 255);
+ }
+ });
+
+ g_lua.registerFunction<std::string(std::shared_ptr<BPFFilter>::*)()>("getStats", [](const std::shared_ptr<BPFFilter> bpf) {
+ setLuaNoSideEffect();
+ std::string res;
+ if (bpf) {
+ std::vector<std::pair<ComboAddress, uint64_t> > stats = bpf->getAddrStats();
+ for (const auto& value : stats) {
+ if (value.first.sin4.sin_family == AF_INET) {
+ res += value.first.toString() + ": " + std::to_string(value.second) + "\n";
+ }
+ else if (value.first.sin4.sin_family == AF_INET6) {
+ res += "[" + value.first.toString() + "]: " + std::to_string(value.second) + "\n";
+ }
+ }
+ std::vector<std::tuple<DNSName, uint16_t, uint64_t> > qstats = bpf->getQNameStats();
+ for (const auto& value : qstats) {
+ res += std::get<0>(value).toString() + " " + std::to_string(std::get<1>(value)) + ": " + std::to_string(std::get<2>(value)) + "\n";
+ }
+ }
+ return res;
+ });
+
+ g_lua.registerFunction<void(std::shared_ptr<BPFFilter>::*)()>("attachToAllBinds", [](std::shared_ptr<BPFFilter> bpf) {
+ std::string res;
+ if (bpf) {
+ for (const auto& frontend : g_frontends) {
+ frontend->attachFilter(bpf);
+ }
+ }
+ });
+
+ g_lua.writeFunction("newDynBPFFilter", [client](std::shared_ptr<BPFFilter> bpf) {
+ if (client) {
+ return std::shared_ptr<DynBPFFilter>(nullptr);
+ }
+ return std::make_shared<DynBPFFilter>(bpf);
+ });
+
+ g_lua.registerFunction<void(std::shared_ptr<DynBPFFilter>::*)(const ComboAddress& addr, boost::optional<int> seconds)>("block", [](std::shared_ptr<DynBPFFilter> dbpf, const ComboAddress& addr, boost::optional<int> seconds) {
+ if (dbpf) {
+ struct timespec until;
+ clock_gettime(CLOCK_MONOTONIC, &until);
+ until.tv_sec += seconds ? *seconds : 10;
+ dbpf->block(addr, until);
+ }
+ });
+
+ g_lua.registerFunction<void(std::shared_ptr<DynBPFFilter>::*)()>("purgeExpired", [](std::shared_ptr<DynBPFFilter> dbpf) {
+ if (dbpf) {
+ struct timespec now;
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ dbpf->purgeExpired(now);
+ }
+ });
+#endif /* HAVE_EBPF */
+}
--- /dev/null
+/*
+ * 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-lua.hh"
+
+#include "statnode.hh"
+
+static std::unordered_map<int, vector<boost::variant<string,double>>> getGenResponses(unsigned int top, boost::optional<int> labels, std::function<bool(const Rings::Response&)> pred)
+{
+ setLuaNoSideEffect();
+ map<DNSName, int> counts;
+ unsigned int total=0;
+ {
+ std::lock_guard<std::mutex> lock(g_rings.respMutex);
+ if(!labels) {
+ for(const auto& a : g_rings.respRing) {
+ if(!pred(a))
+ continue;
+ counts[a.name]++;
+ total++;
+ }
+ }
+ else {
+ unsigned int lab = *labels;
+ for(auto a : g_rings.respRing) {
+ if(!pred(a))
+ continue;
+
+ a.name.trimToLabels(lab);
+ counts[a.name]++;
+ total++;
+ }
+
+ }
+ }
+ // cout<<"Looked at "<<total<<" responses, "<<counts.size()<<" different ones"<<endl;
+ vector<pair<int, DNSName>> rcounts;
+ rcounts.reserve(counts.size());
+ for(const auto& c : counts)
+ rcounts.push_back(make_pair(c.second, c.first.makeLowerCase()));
+
+ sort(rcounts.begin(), rcounts.end(), [](const decltype(rcounts)::value_type& a,
+ const decltype(rcounts)::value_type& b) {
+ return b.first < a.first;
+ });
+
+ std::unordered_map<int, vector<boost::variant<string,double>>> ret;
+ unsigned int count=1, rest=0;
+ for(const auto& rc : rcounts) {
+ if(count==top+1)
+ rest+=rc.first;
+ else
+ ret.insert({count++, {rc.second.toString(), rc.first, 100.0*rc.first/total}});
+ }
+ ret.insert({count, {"Rest", rest, total > 0 ? 100.0*rest/total : 100.0}});
+ return ret;
+}
+
+static map<ComboAddress,int> filterScore(const map<ComboAddress, unsigned int,ComboAddress::addressOnlyLessThan >& counts,
+ double delta, int rate)
+{
+ std::multimap<unsigned int,ComboAddress> score;
+ for(const auto& e : counts)
+ score.insert({e.second, e.first});
+
+ map<ComboAddress,int> ret;
+
+ double lim = delta*rate;
+ for(auto s = score.crbegin(); s != score.crend() && s->first > lim; ++s) {
+ ret[s->second]=s->first;
+ }
+ return ret;
+}
+
+
+typedef std::function<void(const StatNode&, const StatNode::Stat&, const StatNode::Stat&)> statvisitor_t;
+
+static void statNodeRespRing(statvisitor_t visitor, unsigned int seconds)
+{
+ struct timespec cutoff, now;
+ gettime(&now);
+ if (seconds) {
+ cutoff = now;
+ cutoff.tv_sec -= seconds;
+ }
+
+ std::lock_guard<std::mutex> lock(g_rings.respMutex);
+
+ StatNode root;
+ for(const auto& c : g_rings.respRing) {
+ if (now < c.when)
+ continue;
+
+ if (seconds && c.when < cutoff)
+ continue;
+
+ root.submit(c.name, c.dh.rcode, c.requestor);
+ }
+ StatNode::Stat node;
+
+ root.visit([&visitor](const StatNode* node_, const StatNode::Stat& self, const StatNode::Stat& children) {
+ visitor(*node_, self, children);}, node);
+
+}
+
+static vector<pair<unsigned int, std::unordered_map<string,string> > > getRespRing(boost::optional<int> rcode)
+{
+ typedef std::unordered_map<string,string> entry_t;
+ vector<pair<unsigned int, entry_t > > ret;
+ std::lock_guard<std::mutex> lock(g_rings.respMutex);
+
+ entry_t e;
+ unsigned int count=1;
+ for(const auto& c : g_rings.respRing) {
+ if(rcode && (rcode.get() != c.dh.rcode))
+ continue;
+ e["qname"]=c.name.toString();
+ e["rcode"]=std::to_string(c.dh.rcode);
+ ret.push_back(std::make_pair(count,e));
+ count++;
+ }
+ return ret;
+}
+
+typedef map<ComboAddress, unsigned int,ComboAddress::addressOnlyLessThan > counts_t;
+static map<ComboAddress,int> exceedRespGen(int rate, int seconds, std::function<void(counts_t&, const Rings::Response&)> T)
+{
+ counts_t counts;
+ struct timespec cutoff, mintime, now;
+ gettime(&now);
+ cutoff = mintime = now;
+ cutoff.tv_sec -= seconds;
+
+ std::lock_guard<std::mutex> lock(g_rings.respMutex);
+ for(const auto& c : g_rings.respRing) {
+ if(seconds && c.when < cutoff)
+ continue;
+ if(now < c.when)
+ continue;
+
+ T(counts, c);
+ if(c.when < mintime)
+ mintime = c.when;
+ }
+ double delta = seconds ? seconds : DiffTime(now, mintime);
+ return filterScore(counts, delta, rate);
+}
+
+static map<ComboAddress,int> exceedQueryGen(int rate, int seconds, std::function<void(counts_t&, const Rings::Query&)> T)
+{
+ counts_t counts;
+ struct timespec cutoff, mintime, now;
+ gettime(&now);
+ cutoff = mintime = now;
+ cutoff.tv_sec -= seconds;
+
+ ReadLock rl(&g_rings.queryLock);
+ for(const auto& c : g_rings.queryRing) {
+ if(seconds && c.when < cutoff)
+ continue;
+ if(now < c.when)
+ continue;
+ T(counts, c);
+ if(c.when < mintime)
+ mintime = c.when;
+ }
+ double delta = seconds ? seconds : DiffTime(now, mintime);
+ return filterScore(counts, delta, rate);
+}
+
+
+static map<ComboAddress,int> exceedRCode(int rate, int seconds, int rcode)
+{
+ return exceedRespGen(rate, seconds, [rcode](counts_t& counts, const Rings::Response& r)
+ {
+ if(r.dh.rcode == rcode)
+ counts[r.requestor]++;
+ });
+}
+
+static map<ComboAddress,int> exceedRespByterate(int rate, int seconds)
+{
+ return exceedRespGen(rate, seconds, [](counts_t& counts, const Rings::Response& r)
+ {
+ counts[r.requestor]+=r.size;
+ });
+}
+
+void setupLuaInspection()
+{
+ g_lua.writeFunction("topClients", [](boost::optional<unsigned int> top_) {
+ setLuaNoSideEffect();
+ auto top = top_.get_value_or(10);
+ map<ComboAddress, int,ComboAddress::addressOnlyLessThan > counts;
+ unsigned int total=0;
+ {
+ ReadLock rl(&g_rings.queryLock);
+ for(const auto& c : g_rings.queryRing) {
+ counts[c.requestor]++;
+ total++;
+ }
+ }
+ vector<pair<int, ComboAddress>> rcounts;
+ rcounts.reserve(counts.size());
+ for(const auto& c : counts)
+ rcounts.push_back(make_pair(c.second, c.first));
+
+ sort(rcounts.begin(), rcounts.end(), [](const decltype(rcounts)::value_type& a,
+ const decltype(rcounts)::value_type& b) {
+ return b.first < a.first;
+ });
+ unsigned int count=1, rest=0;
+ boost::format fmt("%4d %-40s %4d %4.1f%%\n");
+ for(const auto& rc : rcounts) {
+ if(count==top+1)
+ rest+=rc.first;
+ else
+ g_outputBuffer += (fmt % (count++) % rc.second.toString() % rc.first % (100.0*rc.first/total)).str();
+ }
+ g_outputBuffer += (fmt % (count) % "Rest" % rest % (total > 0 ? 100.0*rest/total : 100.0)).str();
+ });
+
+ g_lua.writeFunction("getTopQueries", [](unsigned int top, boost::optional<int> labels) {
+ setLuaNoSideEffect();
+ map<DNSName, int> counts;
+ unsigned int total=0;
+ if(!labels) {
+ ReadLock rl(&g_rings.queryLock);
+ for(const auto& a : g_rings.queryRing) {
+ counts[a.name]++;
+ total++;
+ }
+ }
+ else {
+ unsigned int lab = *labels;
+ ReadLock rl(&g_rings.queryLock);
+ for(auto a : g_rings.queryRing) {
+ a.name.trimToLabels(lab);
+ counts[a.name]++;
+ total++;
+ }
+ }
+ // cout<<"Looked at "<<total<<" queries, "<<counts.size()<<" different ones"<<endl;
+ vector<pair<int, DNSName>> rcounts;
+ rcounts.reserve(counts.size());
+ for(const auto& c : counts)
+ rcounts.push_back(make_pair(c.second, c.first.makeLowerCase()));
+
+ sort(rcounts.begin(), rcounts.end(), [](const decltype(rcounts)::value_type& a,
+ const decltype(rcounts)::value_type& b) {
+ return b.first < a.first;
+ });
+
+ std::unordered_map<int, vector<boost::variant<string,double>>> ret;
+ unsigned int count=1, rest=0;
+ for(const auto& rc : rcounts) {
+ if(count==top+1)
+ rest+=rc.first;
+ else
+ ret.insert({count++, {rc.second.toString(), rc.first, 100.0*rc.first/total}});
+ }
+ ret.insert({count, {"Rest", rest, total > 0 ? 100.0*rest/total : 100.0}});
+ return ret;
+
+ });
+
+ g_lua.executeCode(R"(function topQueries(top, labels) top = top or 10; for k,v in ipairs(getTopQueries(top,labels)) do show(string.format("%4d %-40s %4d %4.1f%%",k,v[1],v[2], v[3])) end end)");
+
+ g_lua.writeFunction("getResponseRing", []() {
+ setLuaNoSideEffect();
+ decltype(g_rings.respRing) ring;
+ {
+ std::lock_guard<std::mutex> lock(g_rings.respMutex);
+ ring = g_rings.respRing;
+ }
+ vector<std::unordered_map<string, boost::variant<string, unsigned int> > > ret;
+ ret.reserve(ring.size());
+ decltype(ret)::value_type item;
+ for(const auto& r : ring) {
+ item["name"]=r.name.toString();
+ item["qtype"]=r.qtype;
+ item["rcode"]=r.dh.rcode;
+ item["usec"]=r.usec;
+ ret.push_back(item);
+ }
+ return ret;
+ });
+
+ g_lua.writeFunction("getTopResponses", [](unsigned int top, unsigned int kind, boost::optional<int> labels) {
+ return getGenResponses(top, labels, [kind](const Rings::Response& r) { return r.dh.rcode == kind; });
+ });
+
+ g_lua.executeCode(R"(function topResponses(top, kind, labels) top = top or 10; kind = kind or 0; for k,v in ipairs(getTopResponses(top, kind, labels)) do show(string.format("%4d %-40s %4d %4.1f%%",k,v[1],v[2],v[3])) end end)");
+
+
+ g_lua.writeFunction("getSlowResponses", [](unsigned int top, unsigned int msec, boost::optional<int> labels) {
+ return getGenResponses(top, labels, [msec](const Rings::Response& r) { return r.usec > msec*1000; });
+ });
+
+
+ g_lua.executeCode(R"(function topSlow(top, msec, labels) top = top or 10; msec = msec or 500; for k,v in ipairs(getSlowResponses(top, msec, labels)) do show(string.format("%4d %-40s %4d %4.1f%%",k,v[1],v[2],v[3])) end end)");
+
+ g_lua.writeFunction("getTopBandwidth", [](unsigned int top) {
+ setLuaNoSideEffect();
+ return g_rings.getTopBandwidth(top);
+ });
+
+ g_lua.executeCode(R"(function topBandwidth(top) top = top or 10; for k,v in ipairs(getTopBandwidth(top)) do show(string.format("%4d %-40s %4d %4.1f%%",k,v[1],v[2],v[3])) end end)");
+
+ g_lua.writeFunction("delta", []() {
+ setLuaNoSideEffect();
+ // we hold the lua lock already!
+ for(const auto& d : g_confDelta) {
+ struct tm tm;
+ localtime_r(&d.first.tv_sec, &tm);
+ char date[80];
+ strftime(date, sizeof(date)-1, "-- %a %b %d %Y %H:%M:%S %Z\n", &tm);
+ g_outputBuffer += date;
+ g_outputBuffer += d.second + "\n";
+ }
+ });
+
+ g_lua.writeFunction("grepq", [](boost::variant<string, vector<pair<int,string> > > inp, boost::optional<unsigned int> limit) {
+ setLuaNoSideEffect();
+ boost::optional<Netmask> nm;
+ boost::optional<DNSName> dn;
+ int msec=-1;
+
+ vector<string> vec;
+ auto str=boost::get<string>(&inp);
+ if(str)
+ vec.push_back(*str);
+ else {
+ auto v = boost::get<vector<pair<int, string> > >(inp);
+ for(const auto& a: v)
+ vec.push_back(a.second);
+ }
+
+ for(const auto& s : vec) {
+ try
+ {
+ nm = Netmask(s);
+ }
+ catch(...) {
+ if(boost::ends_with(s,"ms") && sscanf(s.c_str(), "%ums", &msec)) {
+ ;
+ }
+ else {
+ try { dn=DNSName(s); }
+ catch(...)
+ {
+ g_outputBuffer = "Could not parse '"+s+"' as domain name or netmask";
+ return;
+ }
+ }
+ }
+ }
+
+ decltype(g_rings.queryRing) qr;
+ decltype(g_rings.respRing) rr;
+ {
+ ReadLock rl(&g_rings.queryLock);
+ qr=g_rings.queryRing;
+ }
+ sort(qr.begin(), qr.end(), [](const decltype(qr)::value_type& a, const decltype(qr)::value_type& b) {
+ return b.when < a.when;
+ });
+ {
+ std::lock_guard<std::mutex> lock(g_rings.respMutex);
+ rr=g_rings.respRing;
+ }
+
+ sort(rr.begin(), rr.end(), [](const decltype(rr)::value_type& a, const decltype(rr)::value_type& b) {
+ return b.when < a.when;
+ });
+
+ unsigned int num=0;
+ struct timespec now;
+ gettime(&now);
+
+ std::multimap<struct timespec, string> out;
+
+ boost::format fmt("%-7.1f %-47s %-12s %-5d %-25s %-5s %-6.1f %-2s %-2s %-2s %s\n");
+ g_outputBuffer+= (fmt % "Time" % "Client" % "Server" % "ID" % "Name" % "Type" % "Lat." % "TC" % "RD" % "AA" % "Rcode").str();
+
+ if(msec==-1) {
+ for(const auto& c : qr) {
+ bool nmmatch=true, dnmatch=true;
+ if(nm)
+ nmmatch = nm->match(c.requestor);
+ if(dn)
+ dnmatch = c.name.isPartOf(*dn);
+ if(nmmatch && dnmatch) {
+ QType qt(c.qtype);
+ out.insert(make_pair(c.when, (fmt % DiffTime(now, c.when) % c.requestor.toStringWithPort() % "" % htons(c.dh.id) % c.name.toString() % qt.getName() % "" % (c.dh.tc ? "TC" : "") % (c.dh.rd? "RD" : "") % (c.dh.aa? "AA" : "") % "Question").str() )) ;
+
+ if(limit && *limit==++num)
+ break;
+ }
+ }
+ }
+ num=0;
+
+
+ string extra;
+ for(const auto& c : rr) {
+ bool nmmatch=true, dnmatch=true, msecmatch=true;
+ if(nm)
+ nmmatch = nm->match(c.requestor);
+ if(dn)
+ dnmatch = c.name.isPartOf(*dn);
+ if(msec != -1)
+ msecmatch=(c.usec/1000 > (unsigned int)msec);
+
+ if(nmmatch && dnmatch && msecmatch) {
+ QType qt(c.qtype);
+ if(!c.dh.rcode)
+ extra=". " +std::to_string(htons(c.dh.ancount))+ " answers";
+ else
+ extra.clear();
+ if(c.usec != std::numeric_limits<decltype(c.usec)>::max())
+ out.insert(make_pair(c.when, (fmt % DiffTime(now, c.when) % c.requestor.toStringWithPort() % c.ds.toStringWithPort() % htons(c.dh.id) % c.name.toString() % qt.getName() % (c.usec/1000.0) % (c.dh.tc ? "TC" : "") % (c.dh.rd? "RD" : "") % (c.dh.aa? "AA" : "") % (RCode::to_s(c.dh.rcode) + extra)).str() )) ;
+ else
+ out.insert(make_pair(c.when, (fmt % DiffTime(now, c.when) % c.requestor.toStringWithPort() % c.ds.toStringWithPort() % htons(c.dh.id) % c.name.toString() % qt.getName() % "T.O" % (c.dh.tc ? "TC" : "") % (c.dh.rd? "RD" : "") % (c.dh.aa? "AA" : "") % (RCode::to_s(c.dh.rcode) + extra)).str() )) ;
+
+ if(limit && *limit==++num)
+ break;
+ }
+ }
+
+ for(const auto& p : out) {
+ g_outputBuffer+=p.second;
+ }
+ });
+
+ g_lua.writeFunction("showResponseLatency", []() {
+ setLuaNoSideEffect();
+ map<double, unsigned int> histo;
+ double bin=100;
+ for(int i=0; i < 15; ++i) {
+ histo[bin];
+ bin*=2;
+ }
+
+ double totlat=0;
+ unsigned int size=0;
+ {
+ std::lock_guard<std::mutex> lock(g_rings.respMutex);
+ for(const auto& r : g_rings.respRing) {
+ /* skip actively discovered timeouts */
+ if (r.usec == std::numeric_limits<unsigned int>::max())
+ continue;
+
+ ++size;
+ auto iter = histo.lower_bound(r.usec);
+ if(iter != histo.end())
+ iter->second++;
+ else
+ histo.rbegin()++;
+ totlat+=r.usec;
+ }
+ }
+
+ if (size == 0) {
+ g_outputBuffer = "No traffic yet.\n";
+ return;
+ }
+
+ g_outputBuffer = (boost::format("Average response latency: %.02f msec\n") % (0.001*totlat/size)).str();
+ double highest=0;
+
+ for(auto iter = histo.cbegin(); iter != histo.cend(); ++iter) {
+ highest=std::max(highest, iter->second*1.0);
+ }
+ boost::format fmt("%7.2f\t%s\n");
+ g_outputBuffer += (fmt % "msec" % "").str();
+
+ for(auto iter = histo.cbegin(); iter != histo.cend(); ++iter) {
+ int stars = (70.0 * iter->second/highest);
+ char c='*';
+ if(!stars && iter->second) {
+ stars=1; // you get 1 . to show something is there..
+ if(70.0*iter->second/highest > 0.5)
+ c=':';
+ else
+ c='.';
+ }
+ g_outputBuffer += (fmt % (iter->first/1000.0) % string(stars, c)).str();
+ }
+ });
+
+ g_lua.writeFunction("showTCPStats", [] {
+ setLuaNoSideEffect();
+ boost::format fmt("%-10d %-10d %-10d %-10d\n");
+ g_outputBuffer += (fmt % "Clients" % "MaxClients" % "Queued" % "MaxQueued").str();
+ g_outputBuffer += (fmt % g_tcpclientthreads->getThreadsCount() % g_maxTCPClientThreads % g_tcpclientthreads->getQueuedCount() % g_maxTCPQueuedConnections).str();
+ g_outputBuffer += "Query distribution mode is: " + std::string(g_useTCPSinglePipe ? "single queue" : "per-thread queues") + "\n";
+ });
+
+ g_lua.writeFunction("dumpStats", [] {
+ setLuaNoSideEffect();
+ vector<string> leftcolumn, rightcolumn;
+
+ boost::format fmt("%-23s\t%+11s");
+ g_outputBuffer.clear();
+ auto entries = g_stats.entries;
+ sort(entries.begin(), entries.end(),
+ [](const decltype(entries)::value_type& a, const decltype(entries)::value_type& b) {
+ return a.first < b.first;
+ });
+ boost::format flt(" %9.1f");
+ for(const auto& e : entries) {
+ string second;
+ if(const auto& val = boost::get<DNSDistStats::stat_t*>(&e.second))
+ second=std::to_string((*val)->load());
+ else if (const auto& dval = boost::get<double*>(&e.second))
+ second=(flt % (**dval)).str();
+ else
+ second=std::to_string((*boost::get<DNSDistStats::statfunction_t>(&e.second))(e.first));
+
+ if(leftcolumn.size() < g_stats.entries.size()/2)
+ leftcolumn.push_back((fmt % e.first % second).str());
+ else
+ rightcolumn.push_back((fmt % e.first % second).str());
+ }
+
+ auto leftiter=leftcolumn.begin(), rightiter=rightcolumn.begin();
+ boost::format clmn("%|0t|%1% %|39t|%2%\n");
+
+ for(;leftiter != leftcolumn.end() || rightiter != rightcolumn.end();) {
+ string lentry, rentry;
+ if(leftiter!= leftcolumn.end()) {
+ lentry = *leftiter;
+ leftiter++;
+ }
+ if(rightiter!= rightcolumn.end()) {
+ rentry = *rightiter;
+ rightiter++;
+ }
+ g_outputBuffer += (clmn % lentry % rentry).str();
+ }
+ });
+
+ g_lua.writeFunction("exceedServFails", [](unsigned int rate, int seconds) {
+ setLuaNoSideEffect();
+ return exceedRCode(rate, seconds, RCode::ServFail);
+ });
+ g_lua.writeFunction("exceedNXDOMAINs", [](unsigned int rate, int seconds) {
+ setLuaNoSideEffect();
+ return exceedRCode(rate, seconds, RCode::NXDomain);
+ });
+
+ g_lua.writeFunction("exceedRespByterate", [](unsigned int rate, int seconds) {
+ setLuaNoSideEffect();
+ return exceedRespByterate(rate, seconds);
+ });
+
+ g_lua.writeFunction("exceedQTypeRate", [](uint16_t type, unsigned int rate, int seconds) {
+ setLuaNoSideEffect();
+ return exceedQueryGen(rate, seconds, [type](counts_t& counts, const Rings::Query& q) {
+ if(q.qtype==type)
+ counts[q.requestor]++;
+ });
+ });
+
+ g_lua.writeFunction("exceedQRate", [](unsigned int rate, int seconds) {
+ setLuaNoSideEffect();
+ return exceedQueryGen(rate, seconds, [](counts_t& counts, const Rings::Query& q) {
+ counts[q.requestor]++;
+ });
+ });
+
+ g_lua.writeFunction("getRespRing", getRespRing);
+
+ /* StatNode */
+ g_lua.registerFunction<StatNode, unsigned int()>("numChildren",
+ [](StatNode& sn) -> unsigned int {
+ return sn.children.size();
+ } );
+ g_lua.registerMember("fullname", &StatNode::fullname);
+ g_lua.registerMember("labelsCount", &StatNode::labelsCount);
+ g_lua.registerMember("servfails", &StatNode::Stat::servfails);
+ g_lua.registerMember("nxdomains", &StatNode::Stat::nxdomains);
+ g_lua.registerMember("queries", &StatNode::Stat::queries);
+
+ g_lua.writeFunction("statNodeRespRing", [](statvisitor_t visitor, boost::optional<unsigned int> seconds) {
+ statNodeRespRing(visitor, seconds ? *seconds : 0);
+ });
+}
--- /dev/null
+/*
+ * 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-lua.hh"
+
+#include "dnsparser.hh"
+
+class MaxQPSIPRule : public DNSRule
+{
+public:
+ MaxQPSIPRule(unsigned int qps, unsigned int burst, unsigned int ipv4trunc=32, unsigned int ipv6trunc=64) :
+ d_qps(qps), d_burst(burst), d_ipv4trunc(ipv4trunc), d_ipv6trunc(ipv6trunc)
+ {
+ pthread_rwlock_init(&d_lock, 0);
+ }
+
+ bool matches(const DNSQuestion* dq) const override
+ {
+ ComboAddress zeroport(*dq->remote);
+ zeroport.sin4.sin_port=0;
+ zeroport.truncate(zeroport.sin4.sin_family == AF_INET ? d_ipv4trunc : d_ipv6trunc);
+ {
+ ReadLock r(&d_lock);
+ const auto iter = d_limits.find(zeroport);
+ if (iter != d_limits.end()) {
+ return !iter->second.check();
+ }
+ }
+ {
+ WriteLock w(&d_lock);
+ auto iter = d_limits.find(zeroport);
+ if(iter == d_limits.end()) {
+ iter=d_limits.insert({zeroport,QPSLimiter(d_qps, d_burst)}).first;
+ }
+ return !iter->second.check();
+ }
+ }
+
+ string toString() const override
+ {
+ return "IP (/"+std::to_string(d_ipv4trunc)+", /"+std::to_string(d_ipv6trunc)+") match for QPS over " + std::to_string(d_qps) + " burst "+ std::to_string(d_burst);
+ }
+
+
+private:
+ mutable pthread_rwlock_t d_lock;
+ mutable std::map<ComboAddress, QPSLimiter> d_limits;
+ unsigned int d_qps, d_burst, d_ipv4trunc, d_ipv6trunc;
+
+};
+
+class MaxQPSRule : public DNSRule
+{
+public:
+ MaxQPSRule(unsigned int qps)
+ : d_qps(qps, qps)
+ {}
+
+ MaxQPSRule(unsigned int qps, unsigned int burst)
+ : d_qps(qps, burst)
+ {}
+
+
+ bool matches(const DNSQuestion* qd) const override
+ {
+ return d_qps.check();
+ }
+
+ string toString() const override
+ {
+ return "Max " + std::to_string(d_qps.getRate()) + " qps";
+ }
+
+
+private:
+ mutable QPSLimiter d_qps;
+};
+
+class NMGRule : public DNSRule
+{
+public:
+ NMGRule(const NetmaskGroup& nmg) : d_nmg(nmg) {}
+protected:
+ NetmaskGroup d_nmg;
+};
+
+class NetmaskGroupRule : public NMGRule
+{
+public:
+ NetmaskGroupRule(const NetmaskGroup& nmg, bool src) : NMGRule(nmg)
+ {
+ d_src = src;
+ }
+ bool matches(const DNSQuestion* dq) const override
+ {
+ if(!d_src) {
+ return d_nmg.match(*dq->local);
+ }
+ return d_nmg.match(*dq->remote);
+ }
+
+ string toString() const override
+ {
+ if(!d_src) {
+ return "Dst: "+d_nmg.toString();
+ }
+ return "Src: "+d_nmg.toString();
+ }
+private:
+ bool d_src;
+};
+
+class TimedIPSetRule : public DNSRule, boost::noncopyable
+{
+private:
+ struct IPv6 {
+ IPv6(const ComboAddress& ca)
+ {
+ static_assert(sizeof(*this)==16, "IPv6 struct has wrong size");
+ memcpy((char*)this, ca.sin6.sin6_addr.s6_addr, 16);
+ }
+ bool operator==(const IPv6& rhs) const
+ {
+ return a==rhs.a && b==rhs.b;
+ }
+ uint64_t a, b;
+ };
+
+public:
+ TimedIPSetRule()
+ {
+ pthread_rwlock_init(&d_lock4, 0);
+ pthread_rwlock_init(&d_lock6, 0);
+ }
+ bool matches(const DNSQuestion* dq) const override
+ {
+ if(dq->remote->sin4.sin_family == AF_INET) {
+ ReadLock rl(&d_lock4);
+ auto fnd = d_ip4s.find(dq->remote->sin4.sin_addr.s_addr);
+ if(fnd == d_ip4s.end()) {
+ return false;
+ }
+ return time(0) < fnd->second;
+ } else {
+ ReadLock rl(&d_lock6);
+ auto fnd = d_ip6s.find({*dq->remote});
+ if(fnd == d_ip6s.end()) {
+ return false;
+ }
+ return time(0) < fnd->second;
+ }
+ }
+
+ void add(const ComboAddress& ca, time_t ttd)
+ {
+ // think twice before adding templates here
+ if(ca.sin4.sin_family == AF_INET) {
+ WriteLock rl(&d_lock4);
+ auto res=d_ip4s.insert({ca.sin4.sin_addr.s_addr, ttd});
+ if(!res.second && (time_t)res.first->second < ttd)
+ res.first->second = (uint32_t)ttd;
+ }
+ else {
+ WriteLock rl(&d_lock6);
+ auto res=d_ip6s.insert({{ca}, ttd});
+ if(!res.second && (time_t)res.first->second < ttd)
+ res.first->second = (uint32_t)ttd;
+ }
+ }
+
+ void remove(const ComboAddress& ca)
+ {
+ if(ca.sin4.sin_family == AF_INET) {
+ WriteLock rl(&d_lock4);
+ d_ip4s.erase(ca.sin4.sin_addr.s_addr);
+ }
+ else {
+ WriteLock rl(&d_lock6);
+ d_ip6s.erase({ca});
+ }
+ }
+
+ void clear()
+ {
+ {
+ WriteLock rl(&d_lock4);
+ d_ip4s.clear();
+ }
+ WriteLock rl(&d_lock6);
+ d_ip6s.clear();
+ }
+
+ void cleanup()
+ {
+ time_t now=time(0);
+ {
+ WriteLock rl(&d_lock4);
+
+ for(auto iter = d_ip4s.begin(); iter != d_ip4s.end(); ) {
+ if(iter->second < now)
+ iter=d_ip4s.erase(iter);
+ else
+ ++iter;
+ }
+
+ }
+
+ {
+ WriteLock rl(&d_lock6);
+
+ for(auto iter = d_ip6s.begin(); iter != d_ip6s.end(); ) {
+ if(iter->second < now)
+ iter=d_ip6s.erase(iter);
+ else
+ ++iter;
+ }
+
+ }
+
+ }
+
+ string toString() const override
+ {
+ time_t now=time(0);
+ uint64_t count = 0;
+ {
+ ReadLock rl(&d_lock4);
+ for(const auto& ip : d_ip4s)
+ if(now < ip.second)
+ ++count;
+ }
+ {
+ ReadLock rl(&d_lock6);
+ for(const auto& ip : d_ip6s)
+ if(now < ip.second)
+ ++count;
+ }
+
+ return "Src: "+std::to_string(count)+" ips";
+ }
+private:
+ struct IPv6Hash
+ {
+ std::size_t operator()(const IPv6& ip) const
+ {
+ auto ah=std::hash<uint64_t>{}(ip.a);
+ auto bh=std::hash<uint64_t>{}(ip.b);
+ return ah & (bh<<1);
+ }
+ };
+ std::unordered_map<IPv6, time_t, IPv6Hash> d_ip6s;
+ std::unordered_map<uint32_t, time_t> d_ip4s;
+ mutable pthread_rwlock_t d_lock4;
+ mutable pthread_rwlock_t d_lock6;
+};
+
+
+class AllRule : public DNSRule
+{
+public:
+ AllRule() {}
+ bool matches(const DNSQuestion* dq) const override
+ {
+ return true;
+ }
+
+ string toString() const override
+ {
+ return "All";
+ }
+
+};
+
+
+class DNSSECRule : public DNSRule
+{
+public:
+ DNSSECRule()
+ {
+
+ }
+ bool matches(const DNSQuestion* dq) const override
+ {
+ return dq->dh->cd || (getEDNSZ((const char*)dq->dh, dq->len) & EDNS_HEADER_FLAG_DO); // turns out dig sets ad by default..
+ }
+
+ string toString() const override
+ {
+ return "DNSSEC";
+ }
+};
+
+class AndRule : public DNSRule
+{
+public:
+ AndRule(const vector<pair<int, shared_ptr<DNSRule> > >& rules)
+ {
+ for(const auto& r : rules)
+ d_rules.push_back(r.second);
+ }
+
+ bool matches(const DNSQuestion* dq) const override
+ {
+ auto iter = d_rules.begin();
+ for(; iter != d_rules.end(); ++iter)
+ if(!(*iter)->matches(dq))
+ break;
+ return iter == d_rules.end();
+ }
+
+ string toString() const override
+ {
+ string ret;
+ for(const auto& rule : d_rules) {
+ if(!ret.empty())
+ ret+= " && ";
+ ret += "("+ rule->toString()+")";
+ }
+ return ret;
+ }
+private:
+
+ vector<std::shared_ptr<DNSRule> > d_rules;
+
+};
+
+
+class OrRule : public DNSRule
+{
+public:
+ OrRule(const vector<pair<int, shared_ptr<DNSRule> > >& rules)
+ {
+ for(const auto& r : rules)
+ d_rules.push_back(r.second);
+ }
+
+ bool matches(const DNSQuestion* dq) const override
+ {
+ auto iter = d_rules.begin();
+ for(; iter != d_rules.end(); ++iter)
+ if((*iter)->matches(dq))
+ return true;
+ return false;
+ }
+
+ string toString() const override
+ {
+ string ret;
+ for(const auto& rule : d_rules) {
+ if(!ret.empty())
+ ret+= " || ";
+ ret += "("+ rule->toString()+")";
+ }
+ return ret;
+ }
+private:
+
+ vector<std::shared_ptr<DNSRule> > d_rules;
+
+};
+
+
+class RegexRule : public DNSRule
+{
+public:
+ RegexRule(const std::string& regex) : d_regex(regex), d_visual(regex)
+ {
+
+ }
+ bool matches(const DNSQuestion* dq) const override
+ {
+ return d_regex.match(dq->qname->toStringNoDot());
+ }
+
+ string toString() const override
+ {
+ return "Regex: "+d_visual;
+ }
+private:
+ Regex d_regex;
+ string d_visual;
+};
+
+#ifdef HAVE_RE2
+#include <re2/re2.h>
+class RE2Rule : public DNSRule
+{
+public:
+ RE2Rule(const std::string& re2) : d_re2(re2, RE2::Latin1), d_visual(re2)
+ {
+
+ }
+ bool matches(const DNSQuestion* dq) const override
+ {
+ return RE2::FullMatch(dq->qname->toStringNoDot(), d_re2);
+ }
+
+ string toString() const override
+ {
+ return "RE2 match: "+d_visual;
+ }
+private:
+ RE2 d_re2;
+ string d_visual;
+};
+#endif
+
+
+class SuffixMatchNodeRule : public DNSRule
+{
+public:
+ SuffixMatchNodeRule(const SuffixMatchNode& smn, bool quiet=false) : d_smn(smn), d_quiet(quiet)
+ {
+ }
+ bool matches(const DNSQuestion* dq) const override
+ {
+ return d_smn.check(*dq->qname);
+ }
+ string toString() const override
+ {
+ if(d_quiet)
+ return "qname==in-set";
+ else
+ return "qname in "+d_smn.toString();
+ }
+private:
+ SuffixMatchNode d_smn;
+ bool d_quiet;
+};
+
+class QNameRule : public DNSRule
+{
+public:
+ QNameRule(const DNSName& qname) : d_qname(qname)
+ {
+ }
+ bool matches(const DNSQuestion* dq) const override
+ {
+ return d_qname==*dq->qname;
+ }
+ string toString() const override
+ {
+ return "qname=="+d_qname.toString();
+ }
+private:
+ DNSName d_qname;
+};
+
+
+class QTypeRule : public DNSRule
+{
+public:
+ QTypeRule(uint16_t qtype) : d_qtype(qtype)
+ {
+ }
+ bool matches(const DNSQuestion* dq) const override
+ {
+ return d_qtype == dq->qtype;
+ }
+ string toString() const override
+ {
+ QType qt(d_qtype);
+ return "qtype=="+qt.getName();
+ }
+private:
+ uint16_t d_qtype;
+};
+
+class QClassRule : public DNSRule
+{
+public:
+ QClassRule(uint16_t qclass) : d_qclass(qclass)
+ {
+ }
+ bool matches(const DNSQuestion* dq) const override
+ {
+ return d_qclass == dq->qclass;
+ }
+ string toString() const override
+ {
+ return "qclass=="+std::to_string(d_qclass);
+ }
+private:
+ uint16_t d_qclass;
+};
+
+class OpcodeRule : public DNSRule
+{
+public:
+ OpcodeRule(uint8_t opcode) : d_opcode(opcode)
+ {
+ }
+ bool matches(const DNSQuestion* dq) const override
+ {
+ return d_opcode == dq->dh->opcode;
+ }
+ string toString() const override
+ {
+ return "opcode=="+std::to_string(d_opcode);
+ }
+private:
+ uint8_t d_opcode;
+};
+
+class TCPRule : public DNSRule
+{
+public:
+ TCPRule(bool tcp): d_tcp(tcp)
+ {
+ }
+ bool matches(const DNSQuestion* dq) const override
+ {
+ return dq->tcp == d_tcp;
+ }
+ string toString() const override
+ {
+ return (d_tcp ? "TCP" : "UDP");
+ }
+private:
+ bool d_tcp;
+};
+
+
+class NotRule : public DNSRule
+{
+public:
+ NotRule(shared_ptr<DNSRule>& rule): d_rule(rule)
+ {
+ }
+ bool matches(const DNSQuestion* dq) const override
+ {
+ return !d_rule->matches(dq);
+ }
+ string toString() const override
+ {
+ return "!("+ d_rule->toString()+")";
+ }
+private:
+ shared_ptr<DNSRule> d_rule;
+};
+
+class RecordsCountRule : public DNSRule
+{
+public:
+ RecordsCountRule(uint8_t section, uint16_t minCount, uint16_t maxCount): d_minCount(minCount), d_maxCount(maxCount), d_section(section)
+ {
+ }
+ bool matches(const DNSQuestion* dq) const override
+ {
+ uint16_t count = 0;
+ switch(d_section) {
+ case 0:
+ count = ntohs(dq->dh->qdcount);
+ break;
+ case 1:
+ count = ntohs(dq->dh->ancount);
+ break;
+ case 2:
+ count = ntohs(dq->dh->nscount);
+ break;
+ case 3:
+ count = ntohs(dq->dh->arcount);
+ break;
+ }
+ return count >= d_minCount && count <= d_maxCount;
+ }
+ string toString() const override
+ {
+ string section;
+ switch(d_section) {
+ case 0:
+ section = "QD";
+ break;
+ case 1:
+ section = "AN";
+ break;
+ case 2:
+ section = "NS";
+ break;
+ case 3:
+ section = "AR";
+ break;
+ }
+ return std::to_string(d_minCount) + " <= records in " + section + " <= "+ std::to_string(d_maxCount);
+ }
+private:
+ uint16_t d_minCount;
+ uint16_t d_maxCount;
+ uint8_t d_section;
+};
+
+class RecordsTypeCountRule : public DNSRule
+{
+public:
+ RecordsTypeCountRule(uint8_t section, uint16_t type, uint16_t minCount, uint16_t maxCount): d_type(type), d_minCount(minCount), d_maxCount(maxCount), d_section(section)
+ {
+ }
+ bool matches(const DNSQuestion* dq) const override
+ {
+ uint16_t count = 0;
+ switch(d_section) {
+ case 0:
+ count = ntohs(dq->dh->qdcount);
+ break;
+ case 1:
+ count = ntohs(dq->dh->ancount);
+ break;
+ case 2:
+ count = ntohs(dq->dh->nscount);
+ break;
+ case 3:
+ count = ntohs(dq->dh->arcount);
+ break;
+ }
+ if (count < d_minCount) {
+ return false;
+ }
+ count = getRecordsOfTypeCount(reinterpret_cast<const char*>(dq->dh), dq->len, d_section, d_type);
+ return count >= d_minCount && count <= d_maxCount;
+ }
+ string toString() const override
+ {
+ string section;
+ switch(d_section) {
+ case 0:
+ section = "QD";
+ break;
+ case 1:
+ section = "AN";
+ break;
+ case 2:
+ section = "NS";
+ break;
+ case 3:
+ section = "AR";
+ break;
+ }
+ return std::to_string(d_minCount) + " <= " + QType(d_type).getName() + " records in " + section + " <= "+ std::to_string(d_maxCount);
+ }
+private:
+ uint16_t d_type;
+ uint16_t d_minCount;
+ uint16_t d_maxCount;
+ uint8_t d_section;
+};
+
+class TrailingDataRule : public DNSRule
+{
+public:
+ TrailingDataRule()
+ {
+ }
+ bool matches(const DNSQuestion* dq) const override
+ {
+ uint16_t length = getDNSPacketLength(reinterpret_cast<const char*>(dq->dh), dq->len);
+ return length < dq->len;
+ }
+ string toString() const override
+ {
+ return "trailing data";
+ }
+};
+
+class QNameLabelsCountRule : public DNSRule
+{
+public:
+ QNameLabelsCountRule(unsigned int minLabelsCount, unsigned int maxLabelsCount): d_min(minLabelsCount), d_max(maxLabelsCount)
+ {
+ }
+ bool matches(const DNSQuestion* dq) const override
+ {
+ unsigned int count = dq->qname->countLabels();
+ return count < d_min || count > d_max;
+ }
+ string toString() const override
+ {
+ return "labels count < " + std::to_string(d_min) + " || labels count > " + std::to_string(d_max);
+ }
+private:
+ unsigned int d_min;
+ unsigned int d_max;
+};
+
+class QNameWireLengthRule : public DNSRule
+{
+public:
+ QNameWireLengthRule(size_t min, size_t max): d_min(min), d_max(max)
+ {
+ }
+ bool matches(const DNSQuestion* dq) const override
+ {
+ size_t const wirelength = dq->qname->wirelength();
+ return wirelength < d_min || wirelength > d_max;
+ }
+ string toString() const override
+ {
+ return "wire length < " + std::to_string(d_min) + " || wire length > " + std::to_string(d_max);
+ }
+private:
+ size_t d_min;
+ size_t d_max;
+};
+
+class RCodeRule : public DNSRule
+{
+public:
+ RCodeRule(int rcode) : d_rcode(rcode)
+ {
+ }
+ bool matches(const DNSQuestion* dq) const override
+ {
+ return d_rcode == dq->dh->rcode;
+ }
+ string toString() const override
+ {
+ return "rcode=="+RCode::to_s(d_rcode);
+ }
+private:
+ int d_rcode;
+};
+
+class RDRule : public DNSRule
+{
+public:
+ RDRule()
+ {
+ }
+ bool matches(const DNSQuestion* dq) const override
+ {
+ return dq->dh->rd == 1;
+ }
+ string toString() const override
+ {
+ return "rd==1";
+ }
+};
+
+class ProbaRule : public DNSRule
+{
+public:
+ ProbaRule(double proba) : d_proba(proba)
+ {
+ }
+ bool matches(const DNSQuestion* dq) const override
+ {
+ if(d_proba == 1.0)
+ return true;
+ double rnd = 1.0*random() / RAND_MAX;
+ return rnd > (1.0 - d_proba);
+ }
+ string toString() const override
+ {
+ return "match with prob. " + (boost::format("%0.2f") % d_proba).str();
+ }
+private:
+ double d_proba;
+};
+
+class TagRule : public DNSRule
+{
+public:
+ TagRule(std::string tag, boost::optional<std::string> value) : d_value(value), d_tag(tag)
+ {
+ }
+ bool matches(const DNSQuestion* dq) const override
+ {
+ if (dq->qTag == nullptr) {
+ return false;
+ }
+
+ const auto got = dq->qTag->tagData.find(d_tag);
+ if (got == dq->qTag->tagData.cend()) {
+ return false;
+ }
+
+ if (!d_value) {
+ return true;
+ }
+
+ return got->second == *d_value;
+ }
+
+ string toString() const override
+ {
+ return "tag '" + d_tag + "' is set" + (d_value ? (" to '" + *d_value + "'") : "");
+ }
+
+private:
+ boost::optional<std::string> d_value;
+ std::string d_tag;
+};
+
+std::shared_ptr<DNSRule> makeRule(const luadnsrule_t& var)
+{
+ if (var.type() == typeid(std::shared_ptr<DNSRule>))
+ return *boost::get<std::shared_ptr<DNSRule>>(&var);
+
+ SuffixMatchNode smn;
+ NetmaskGroup nmg;
+ auto add=[&](string src) {
+ try {
+ nmg.addMask(src); // need to try mask first, all masks are domain names!
+ } catch(...) {
+ smn.add(DNSName(src));
+ }
+ };
+
+ if (var.type() == typeid(string))
+ add(*boost::get<string>(&var));
+
+ else if (var.type() == typeid(vector<pair<int, string>>))
+ for(const auto& a : *boost::get<vector<pair<int, string>>>(&var))
+ add(a.second);
+
+ else if (var.type() == typeid(DNSName))
+ smn.add(*boost::get<DNSName>(&var));
+
+ else if (var.type() == typeid(vector<pair<int, DNSName>>))
+ for(const auto& a : *boost::get<vector<pair<int, DNSName>>>(&var))
+ smn.add(a.second);
+
+ if(nmg.empty())
+ return std::make_shared<SuffixMatchNodeRule>(smn);
+ else
+ return std::make_shared<NetmaskGroupRule>(nmg, true);
+}
+
+void setupLuaRules()
+{
+ g_lua.writeFunction("makeRule", makeRule);
+
+ g_lua.registerFunction<string(std::shared_ptr<DNSRule>::*)()>("toString", [](const std::shared_ptr<DNSRule>& rule) { return rule->toString(); });
+
+ g_lua.writeFunction("showResponseRules", []() {
+ setLuaNoSideEffect();
+ boost::format fmt("%-3d %9d %-50s %s\n");
+ g_outputBuffer += (fmt % "#" % "Matches" % "Rule" % "Action").str();
+ int num=0;
+ for(const auto& lim : g_resprulactions.getCopy()) {
+ string name = lim.first->toString();
+ g_outputBuffer += (fmt % num % lim.first->d_matches % name % lim.second->toString()).str();
+ ++num;
+ }
+ });
+
+ g_lua.writeFunction("rmResponseRule", [](unsigned int num) {
+ setLuaSideEffect();
+ auto rules = g_resprulactions.getCopy();
+ if(num >= rules.size()) {
+ g_outputBuffer = "Error: attempt to delete non-existing rule\n";
+ return;
+ }
+ rules.erase(rules.begin()+num);
+ g_resprulactions.setState(rules);
+ });
+
+ g_lua.writeFunction("topResponseRule", []() {
+ setLuaSideEffect();
+ auto rules = g_resprulactions.getCopy();
+ if(rules.empty())
+ return;
+ auto subject = *rules.rbegin();
+ rules.erase(std::prev(rules.end()));
+ rules.insert(rules.begin(), subject);
+ g_resprulactions.setState(rules);
+ });
+
+ g_lua.writeFunction("mvResponseRule", [](unsigned int from, unsigned int to) {
+ setLuaSideEffect();
+ auto rules = g_resprulactions.getCopy();
+ if(from >= rules.size() || to > rules.size()) {
+ g_outputBuffer = "Error: attempt to move rules from/to invalid index\n";
+ return;
+ }
+ auto subject = rules[from];
+ rules.erase(rules.begin()+from);
+ if(to == rules.size())
+ rules.push_back(subject);
+ else {
+ if(from < to)
+ --to;
+ rules.insert(rules.begin()+to, subject);
+ }
+ g_resprulactions.setState(rules);
+ });
+
+ g_lua.writeFunction("showCacheHitResponseRules", []() {
+ setLuaNoSideEffect();
+ boost::format fmt("%-3d %9d %-50s %s\n");
+ g_outputBuffer += (fmt % "#" % "Matches" % "Rule" % "Action").str();
+ int num=0;
+ for(const auto& lim : g_cachehitresprulactions.getCopy()) {
+ string name = lim.first->toString();
+ g_outputBuffer += (fmt % num % lim.first->d_matches % name % lim.second->toString()).str();
+ ++num;
+ }
+ });
+
+ g_lua.writeFunction("rmCacheHitResponseRule", [](unsigned int num) {
+ setLuaSideEffect();
+ auto rules = g_cachehitresprulactions.getCopy();
+ if(num >= rules.size()) {
+ g_outputBuffer = "Error: attempt to delete non-existing rule\n";
+ return;
+ }
+ rules.erase(rules.begin()+num);
+ g_cachehitresprulactions.setState(rules);
+ });
+
+ g_lua.writeFunction("topCacheHitResponseRule", []() {
+ setLuaSideEffect();
+ auto rules = g_cachehitresprulactions.getCopy();
+ if(rules.empty())
+ return;
+ auto subject = *rules.rbegin();
+ rules.erase(std::prev(rules.end()));
+ rules.insert(rules.begin(), subject);
+ g_cachehitresprulactions.setState(rules);
+ });
+
+ g_lua.writeFunction("mvCacheHitResponseRule", [](unsigned int from, unsigned int to) {
+ setLuaSideEffect();
+ auto rules = g_cachehitresprulactions.getCopy();
+ if(from >= rules.size() || to > rules.size()) {
+ g_outputBuffer = "Error: attempt to move rules from/to invalid index\n";
+ return;
+ }
+ auto subject = rules[from];
+ rules.erase(rules.begin()+from);
+ if(to == rules.size())
+ rules.push_back(subject);
+ else {
+ if(from < to)
+ --to;
+ rules.insert(rules.begin()+to, subject);
+ }
+ g_cachehitresprulactions.setState(rules);
+ });
+
+ g_lua.writeFunction("rmRule", [](unsigned int num) {
+ setLuaSideEffect();
+ auto rules = g_rulactions.getCopy();
+ if(num >= rules.size()) {
+ g_outputBuffer = "Error: attempt to delete non-existing rule\n";
+ return;
+ }
+ rules.erase(rules.begin()+num);
+ g_rulactions.setState(rules);
+ });
+
+ g_lua.writeFunction("topRule", []() {
+ setLuaSideEffect();
+ auto rules = g_rulactions.getCopy();
+ if(rules.empty())
+ return;
+ auto subject = *rules.rbegin();
+ rules.erase(std::prev(rules.end()));
+ rules.insert(rules.begin(), subject);
+ g_rulactions.setState(rules);
+ });
+
+ g_lua.writeFunction("mvRule", [](unsigned int from, unsigned int to) {
+ setLuaSideEffect();
+ auto rules = g_rulactions.getCopy();
+ if(from >= rules.size() || to > rules.size()) {
+ g_outputBuffer = "Error: attempt to move rules from/to invalid index\n";
+ return;
+ }
+
+ auto subject = rules[from];
+ rules.erase(rules.begin()+from);
+ if(to == rules.size())
+ rules.push_back(subject);
+ else {
+ if(from < to)
+ --to;
+ rules.insert(rules.begin()+to, subject);
+ }
+ g_rulactions.setState(rules);
+ });
+
+ g_lua.writeFunction("clearRules", []() {
+ setLuaSideEffect();
+ g_rulactions.modify([](decltype(g_rulactions)::value_type& rulactions) {
+ rulactions.clear();
+ });
+ });
+
+ g_lua.writeFunction("setRules", [](std::vector< std::pair<int, std::shared_ptr<std::pair<luadnsrule_t, std::shared_ptr<DNSAction> > > > > newruleactions) {
+ setLuaSideEffect();
+ g_rulactions.modify([newruleactions](decltype(g_rulactions)::value_type& gruleactions) {
+ gruleactions.clear();
+ for (const auto& newruleaction : newruleactions) {
+ if (newruleaction.second) {
+ auto rule=makeRule(newruleaction.second->first);
+ gruleactions.push_back({rule, newruleaction.second->second});
+ }
+ }
+ });
+ });
+
+ g_lua.writeFunction("MaxQPSIPRule", [](unsigned int qps, boost::optional<int> ipv4trunc, boost::optional<int> ipv6trunc, boost::optional<int> burst) {
+ return std::shared_ptr<DNSRule>(new MaxQPSIPRule(qps, burst.get_value_or(qps), ipv4trunc.get_value_or(32), ipv6trunc.get_value_or(64)));
+ });
+
+ g_lua.writeFunction("MaxQPSRule", [](unsigned int qps, boost::optional<int> burst) {
+ if(!burst)
+ return std::shared_ptr<DNSRule>(new MaxQPSRule(qps));
+ else
+ return std::shared_ptr<DNSRule>(new MaxQPSRule(qps, *burst));
+ });
+
+ g_lua.writeFunction("RegexRule", [](const std::string& str) {
+ return std::shared_ptr<DNSRule>(new RegexRule(str));
+ });
+
+#ifdef HAVE_RE2
+ g_lua.writeFunction("RE2Rule", [](const std::string& str) {
+ return std::shared_ptr<DNSRule>(new RE2Rule(str));
+ });
+#endif
+
+ g_lua.writeFunction("SuffixMatchNodeRule", [](const SuffixMatchNode& smn, boost::optional<bool> quiet) {
+ return std::shared_ptr<DNSRule>(new SuffixMatchNodeRule(smn, quiet ? *quiet : false));
+ });
+
+ g_lua.writeFunction("NetmaskGroupRule", [](const NetmaskGroup& nmg, boost::optional<bool> src) {
+ return std::shared_ptr<DNSRule>(new NetmaskGroupRule(nmg, src ? *src : true));
+ });
+
+ g_lua.writeFunction("benchRule", [](std::shared_ptr<DNSRule> rule, boost::optional<int> times_, boost::optional<string> suffix_) {
+ setLuaNoSideEffect();
+ int times = times_.get_value_or(100000);
+ DNSName suffix(suffix_.get_value_or("powerdns.com"));
+ struct item {
+ vector<uint8_t> packet;
+ ComboAddress rem;
+ DNSName qname;
+ uint16_t qtype, qclass;
+ };
+ vector<item> items;
+ items.reserve(1000);
+ for(int n=0; n < 1000; ++n) {
+ struct item i;
+ i.qname=DNSName(std::to_string(random()));
+ i.qname += suffix;
+ i.qtype = random() % 0xff;
+ i.qclass = 1;
+ i.rem=ComboAddress("127.0.0.1");
+ i.rem.sin4.sin_addr.s_addr = random();
+ DNSPacketWriter pw(i.packet, i.qname, i.qtype);
+ items.push_back(i);
+ }
+
+ int matches=0;
+ ComboAddress dummy("127.0.0.1");
+ DTime dt;
+ dt.set();
+ for(int n=0; n < times; ++n) {
+ const item& i = items[n % items.size()];
+ DNSQuestion dq(&i.qname, i.qtype, i.qclass, &i.rem, &i.rem, (struct dnsheader*)&i.packet[0], i.packet.size(), i.packet.size(), false);
+ if(rule->matches(&dq))
+ matches++;
+ }
+ double udiff=dt.udiff();
+ g_outputBuffer=(boost::format("Had %d matches out of %d, %.1f qps, in %.1f usec\n") % matches % times % (1000000*(1.0*times/udiff)) % udiff).str();
+
+ });
+
+ g_lua.writeFunction("AllRule", []() {
+ return std::shared_ptr<DNSRule>(new AllRule());
+ });
+
+ g_lua.writeFunction("ProbaRule", [](double proba) {
+ return std::shared_ptr<DNSRule>(new ProbaRule(proba));
+ });
+
+ g_lua.writeFunction("QNameRule", [](const std::string& qname) {
+ return std::shared_ptr<DNSRule>(new QNameRule(DNSName(qname)));
+ });
+
+ g_lua.writeFunction("QTypeRule", [](boost::variant<int, std::string> str) {
+ uint16_t qtype;
+ if(auto dir = boost::get<int>(&str)) {
+ qtype = *dir;
+ }
+ else {
+ string val=boost::get<string>(str);
+ qtype = QType::chartocode(val.c_str());
+ if(!qtype)
+ throw std::runtime_error("Unable to convert '"+val+"' to a DNS type");
+ }
+ return std::shared_ptr<DNSRule>(new QTypeRule(qtype));
+ });
+
+ g_lua.writeFunction("QClassRule", [](int c) {
+ return std::shared_ptr<DNSRule>(new QClassRule(c));
+ });
+
+ g_lua.writeFunction("OpcodeRule", [](uint8_t code) {
+ return std::shared_ptr<DNSRule>(new OpcodeRule(code));
+ });
+
+ g_lua.writeFunction("AndRule", [](vector<pair<int, std::shared_ptr<DNSRule> > >a) {
+ return std::shared_ptr<DNSRule>(new AndRule(a));
+ });
+
+ g_lua.writeFunction("OrRule", [](vector<pair<int, std::shared_ptr<DNSRule> > >a) {
+ return std::shared_ptr<DNSRule>(new OrRule(a));
+ });
+
+ g_lua.writeFunction("TCPRule", [](bool tcp) {
+ return std::shared_ptr<DNSRule>(new TCPRule(tcp));
+ });
+
+ g_lua.writeFunction("DNSSECRule", []() {
+ return std::shared_ptr<DNSRule>(new DNSSECRule());
+ });
+
+ g_lua.writeFunction("NotRule", [](std::shared_ptr<DNSRule>rule) {
+ return std::shared_ptr<DNSRule>(new NotRule(rule));
+ });
+
+ g_lua.writeFunction("RecordsCountRule", [](uint8_t section, uint16_t minCount, uint16_t maxCount) {
+ return std::shared_ptr<DNSRule>(new RecordsCountRule(section, minCount, maxCount));
+ });
+
+ g_lua.writeFunction("RecordsTypeCountRule", [](uint8_t section, uint16_t type, uint16_t minCount, uint16_t maxCount) {
+ return std::shared_ptr<DNSRule>(new RecordsTypeCountRule(section, type, minCount, maxCount));
+ });
+
+ g_lua.writeFunction("TrailingDataRule", []() {
+ return std::shared_ptr<DNSRule>(new TrailingDataRule());
+ });
+
+ g_lua.writeFunction("QNameLabelsCountRule", [](unsigned int minLabelsCount, unsigned int maxLabelsCount) {
+ return std::shared_ptr<DNSRule>(new QNameLabelsCountRule(minLabelsCount, maxLabelsCount));
+ });
+
+ g_lua.writeFunction("QNameWireLengthRule", [](size_t min, size_t max) {
+ return std::shared_ptr<DNSRule>(new QNameWireLengthRule(min, max));
+ });
+
+ g_lua.writeFunction("RCodeRule", [](int rcode) {
+ return std::shared_ptr<DNSRule>(new RCodeRule(rcode));
+ });
+
+ g_lua.writeFunction("showRules", []() {
+ setLuaNoSideEffect();
+ boost::format fmt("%-3d %9d %-50s %s\n");
+ g_outputBuffer += (fmt % "#" % "Matches" % "Rule" % "Action").str();
+ int num=0;
+ for(const auto& lim : g_rulactions.getCopy()) {
+ string name = lim.first->toString();
+ g_outputBuffer += (fmt % num % lim.first->d_matches % name % lim.second->toString()).str();
+ ++num;
+ }
+ });
+
+ g_lua.writeFunction("RDRule", []() {
+ return std::shared_ptr<DNSRule>(new RDRule());
+ });
+
+ g_lua.writeFunction("TagRule", [](std::string tag, boost::optional<std::string> value) {
+ return std::shared_ptr<DNSRule>(new TagRule(tag, value));
+ });
+
+ g_lua.writeFunction("TimedIPSetRule", []() {
+ return std::shared_ptr<TimedIPSetRule>(new TimedIPSetRule());
+ });
+
+ g_lua.registerFunction<void(std::shared_ptr<TimedIPSetRule>::*)()>("clear", [](std::shared_ptr<TimedIPSetRule> tisr) {
+ tisr->clear();
+ });
+
+ g_lua.registerFunction<void(std::shared_ptr<TimedIPSetRule>::*)()>("cleanup", [](std::shared_ptr<TimedIPSetRule> tisr) {
+ tisr->cleanup();
+ });
+
+ g_lua.registerFunction<void(std::shared_ptr<TimedIPSetRule>::*)(const ComboAddress& ca, int t)>("add", [](std::shared_ptr<TimedIPSetRule> tisr, const ComboAddress& ca, int t) {
+ tisr->add(ca, time(0)+t);
+ });
+
+ g_lua.registerFunction<std::shared_ptr<DNSRule>(std::shared_ptr<TimedIPSetRule>::*)()>("slice", [](std::shared_ptr<TimedIPSetRule> tisr) {
+ return std::dynamic_pointer_cast<DNSRule>(tisr);
+ });
+}
--- /dev/null
+/*
+ * 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"
+
+void setupLuaVars()
+{
+ g_lua.writeVariable("DNSAction", std::unordered_map<string,int>{
+ {"Drop", (int)DNSAction::Action::Drop},
+ {"Nxdomain", (int)DNSAction::Action::Nxdomain},
+ {"Refused", (int)DNSAction::Action::Refused},
+ {"Spoof", (int)DNSAction::Action::Spoof},
+ {"Allow", (int)DNSAction::Action::Allow},
+ {"HeaderModify", (int)DNSAction::Action::HeaderModify},
+ {"Pool", (int)DNSAction::Action::Pool},
+ {"None",(int)DNSAction::Action::None},
+ {"Delay", (int)DNSAction::Action::Delay},
+ {"Truncate", (int)DNSAction::Action::Truncate}
+ });
+
+ g_lua.writeVariable("DNSResponseAction", std::unordered_map<string,int>{
+ {"Allow", (int)DNSResponseAction::Action::Allow },
+ {"Delay", (int)DNSResponseAction::Action::Delay },
+ {"HeaderModify", (int)DNSResponseAction::Action::HeaderModify },
+ {"None", (int)DNSResponseAction::Action::None }
+ });
+
+ g_lua.writeVariable("DNSClass", std::unordered_map<string,int>{
+ {"IN", QClass::IN },
+ {"CHAOS", QClass::CHAOS },
+ {"NONE", QClass::NONE },
+ {"ANY", QClass::ANY }
+ });
+
+ g_lua.writeVariable("DNSOpcode", std::unordered_map<string,int>{
+ {"Query", Opcode::Query },
+ {"IQuery", Opcode::IQuery },
+ {"Status", Opcode::Status },
+ {"Notify", Opcode::Notify },
+ {"Update", Opcode::Update }
+ });
+
+ g_lua.writeVariable("DNSSection", std::unordered_map<string,int>{
+ {"Question", 0 },
+ {"Answer", 1 },
+ {"Authority", 2 },
+ {"Additional",3 }
+ });
+
+ vector<pair<string, int> > rcodes = {{"NOERROR", RCode::NoError },
+ {"FORMERR", RCode::FormErr },
+ {"SERVFAIL", RCode::ServFail },
+ {"NXDOMAIN", RCode::NXDomain },
+ {"NOTIMP", RCode::NotImp },
+ {"REFUSED", RCode::Refused },
+ {"YXDOMAIN", RCode::YXDomain },
+ {"YXRRSET", RCode::YXRRSet },
+ {"NXRRSET", RCode::NXRRSet },
+ {"NOTAUTH", RCode::NotAuth },
+ {"NOTZONE", RCode::NotZone }
+ };
+ vector<pair<string, int> > dd;
+ for(const auto& n : QType::names)
+ dd.push_back({n.first, n.second});
+ for(const auto& n : rcodes)
+ dd.push_back({n.first, n.second});
+ g_lua.writeVariable("dnsdist", dd);
+}
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
+
+#include <dirent.h>
+#include <fstream>
+#include <net/if.h>
#include <sys/types.h>
#include <sys/socket.h>
-#include <net/if.h>
-#include "dnsdist.hh"
-#include "dnsrulactions.hh"
+#include <sys/stat.h>
#include <thread>
-#include "dolog.hh"
-#include "sodcrypto.hh"
+
+#include "dnsdist.hh"
+#include "dnsdist-lua.hh"
+
#include "base64.hh"
-#include <fstream>
#include "dnswriter.hh"
+#include "dolog.hh"
#include "lock.hh"
-#include "dnsdist-lua.hh"
+#include "sodcrypto.hh"
+
+#include <boost/logic/tribool.hpp>
#ifdef HAVE_SYSTEMD
#include <systemd/sd-daemon.h>
using std::thread;
-static vector<std::function<void(void)>>* g_launchWork;
+static vector<std::function<void(void)>>* g_launchWork = nullptr;
-class LuaAction : public DNSAction
-{
-public:
- typedef std::function<std::tuple<int, string>(DNSQuestion* dq)> func_t;
- LuaAction(LuaAction::func_t func) : d_func(func)
- {}
-
- Action operator()(DNSQuestion* dq, string* ruleresult) const override
- {
- std::lock_guard<std::mutex> lock(g_luamutex);
- auto ret = d_func(dq);
- if(ruleresult)
- *ruleresult=std::get<1>(ret);
- return (Action)std::get<0>(ret);
- }
-
- string toString() const override
- {
- return "Lua script";
- }
+boost::tribool g_noLuaSideEffect;
+static bool g_included{false};
-private:
- func_t d_func;
-};
-
-class LuaResponseAction : public DNSResponseAction
+/* this is a best effort way to prevent logging calls with no side-effects in the output of delta()
+ Functions can declare setLuaNoSideEffect() and if nothing else does declare a side effect, or nothing
+ has done so before on this invocation, this call won't be part of delta() output */
+void setLuaNoSideEffect()
{
-public:
- typedef std::function<std::tuple<int, string>(DNSResponse* dr)> func_t;
- LuaResponseAction(LuaResponseAction::func_t func) : d_func(func)
- {}
-
- Action operator()(DNSResponse* dr, string* ruleresult) const override
- {
- std::lock_guard<std::mutex> lock(g_luamutex);
- auto ret = d_func(dr);
- if(ruleresult)
- *ruleresult=std::get<1>(ret);
- return (Action)std::get<0>(ret);
- }
-
- string toString() const override
- {
- return "Lua response script";
- }
-
-private:
- func_t d_func;
-};
+ if(g_noLuaSideEffect==false) // there has been a side effect already
+ return;
+ g_noLuaSideEffect=true;
+}
-std::shared_ptr<DNSRule> makeRule(const luadnsrule_t& var)
+void setLuaSideEffect()
{
- if (var.type() == typeid(std::shared_ptr<DNSRule>))
- return *boost::get<std::shared_ptr<DNSRule>>(&var);
-
- SuffixMatchNode smn;
- NetmaskGroup nmg;
- auto add=[&](string src) {
- try {
- nmg.addMask(src); // need to try mask first, all masks are domain names!
- } catch(...) {
- smn.add(DNSName(src));
- }
- };
-
- if (var.type() == typeid(string))
- add(*boost::get<string>(&var));
-
- else if (var.type() == typeid(vector<pair<int, string>>))
- for(const auto& a : *boost::get<vector<pair<int, string>>>(&var))
- add(a.second);
-
- else if (var.type() == typeid(DNSName))
- smn.add(*boost::get<DNSName>(&var));
-
- else if (var.type() == typeid(vector<pair<int, DNSName>>))
- for(const auto& a : *boost::get<vector<pair<int, DNSName>>>(&var))
- smn.add(a.second);
+ g_noLuaSideEffect=false;
+}
- if(nmg.empty())
- return std::make_shared<SuffixMatchNodeRule>(smn);
- else
- return std::make_shared<NetmaskGroupRule>(nmg, true);
+bool getLuaNoSideEffect()
+{
+ return g_noLuaSideEffect==true;
}
-std::unordered_map<int, vector<boost::variant<string,double>>> getGenResponses(unsigned int top, boost::optional<int> labels, std::function<bool(const Rings::Response&)> pred)
+void resetLuaSideEffect()
{
- setLuaNoSideEffect();
- map<DNSName, int> counts;
- unsigned int total=0;
- {
- std::lock_guard<std::mutex> lock(g_rings.respMutex);
- if(!labels) {
- for(const auto& a : g_rings.respRing) {
- if(!pred(a))
- continue;
- counts[a.name]++;
- total++;
- }
- }
- else {
- unsigned int lab = *labels;
- for(auto a : g_rings.respRing) {
- if(!pred(a))
- continue;
-
- a.name.trimToLabels(lab);
- counts[a.name]++;
- total++;
- }
-
- }
- }
- // cout<<"Looked at "<<total<<" responses, "<<counts.size()<<" different ones"<<endl;
- vector<pair<int, DNSName>> rcounts;
- rcounts.reserve(counts.size());
- for(const auto& c : counts)
- rcounts.push_back(make_pair(c.second, c.first.makeLowerCase()));
-
- sort(rcounts.begin(), rcounts.end(), [](const decltype(rcounts)::value_type& a,
- const decltype(rcounts)::value_type& b) {
- return b.first < a.first;
- });
-
- std::unordered_map<int, vector<boost::variant<string,double>>> ret;
- unsigned int count=1, rest=0;
- for(const auto& rc : rcounts) {
- if(count==top+1)
- rest+=rc.first;
- else
- ret.insert({count++, {rc.second.toString(), rc.first, 100.0*rc.first/total}});
- }
- ret.insert({count, {"Rest", rest, total > 0 ? 100.0*rest/total : 100.0}});
- return ret;
+ g_noLuaSideEffect = boost::logic::indeterminate;
}
-void parseLocalBindVars(boost::optional<localbind_t> vars, bool& doTCP, bool& reusePort, int& tcpFastOpenQueueSize, std::string& interface, std::set<int>& cpus)
+typedef std::unordered_map<std::string, boost::variant<bool, int, std::string, std::vector<std::pair<int,int> > > > localbind_t;
+
+static void parseLocalBindVars(boost::optional<localbind_t> vars, bool& doTCP, bool& reusePort, int& tcpFastOpenQueueSize, std::string& interface, std::set<int>& cpus)
{
if (vars) {
if (vars->count("doTCP")) {
}
}
-vector<std::function<void(void)>> setupLua(bool client, const std::string& config)
+void setupLuaConfig(bool client)
{
- g_launchWork= new vector<std::function<void(void)>>();
typedef std::unordered_map<std::string, boost::variant<bool, std::string, vector<pair<int, std::string> > > > newserver_t;
- g_lua.writeVariable("DNSAction", std::unordered_map<string,int>{
- {"Drop", (int)DNSAction::Action::Drop},
- {"Nxdomain", (int)DNSAction::Action::Nxdomain},
- {"Refused", (int)DNSAction::Action::Refused},
- {"Spoof", (int)DNSAction::Action::Spoof},
- {"Allow", (int)DNSAction::Action::Allow},
- {"HeaderModify", (int)DNSAction::Action::HeaderModify},
- {"Pool", (int)DNSAction::Action::Pool},
- {"None",(int)DNSAction::Action::None},
- {"Delay", (int)DNSAction::Action::Delay},
- {"Truncate", (int)DNSAction::Action::Truncate}
- });
-
- g_lua.writeVariable("DNSResponseAction", std::unordered_map<string,int>{
- {"Allow", (int)DNSResponseAction::Action::Allow },
- {"Delay", (int)DNSResponseAction::Action::Delay },
- {"HeaderModify", (int)DNSResponseAction::Action::HeaderModify },
- {"None", (int)DNSResponseAction::Action::None }
- });
-
- g_lua.writeVariable("DNSClass", std::unordered_map<string,int>{
- {"IN", QClass::IN },
- {"CHAOS", QClass::CHAOS },
- {"NONE", QClass::NONE },
- {"ANY", QClass::ANY }
- });
-
- g_lua.writeVariable("DNSOpcode", std::unordered_map<string,int>{
- {"Query", Opcode::Query },
- {"IQuery", Opcode::IQuery },
- {"Status", Opcode::Status },
- {"Notify", Opcode::Notify },
- {"Update", Opcode::Update }
- });
-
- g_lua.writeVariable("DNSSection", std::unordered_map<string,int>{
- {"Question", 0 },
- {"Answer", 1 },
- {"Authority", 2 },
- {"Additional",3 }
- });
-
- vector<pair<string, int> > rcodes = {{"NOERROR", RCode::NoError },
- {"FORMERR", RCode::FormErr },
- {"SERVFAIL", RCode::ServFail },
- {"NXDOMAIN", RCode::NXDomain },
- {"NOTIMP", RCode::NotImp },
- {"REFUSED", RCode::Refused },
- {"YXDOMAIN", RCode::YXDomain },
- {"YXRRSET", RCode::YXRRSet },
- {"NXRRSET", RCode::NXRRSet },
- {"NOTAUTH", RCode::NotAuth },
- {"NOTZONE", RCode::NotZone }
- };
- vector<pair<string, int> > dd;
- for(const auto& n : QType::names)
- dd.push_back({n.first, n.second});
- for(const auto& n : rcodes)
- dd.push_back({n.first, n.second});
- g_lua.writeVariable("dnsdist", dd);
-
g_lua.writeFunction("inClientStartup", [client]() {
return client && !g_configurationDone;
});
-
- g_lua.writeFunction("newServer",
- [client](boost::variant<string,newserver_t> pvars, boost::optional<int> qps)
- {
+ g_lua.writeFunction("newServer",
+ [client](boost::variant<string,newserver_t> pvars, boost::optional<int> qps) {
setLuaSideEffect();
if(client) {
return std::make_shared<DownstreamState>(ComboAddress());
if(qps) {
ret->qps=QPSLimiter(*qps, *qps);
}
- g_dstates.modify([ret](servers_t& servers) {
- servers.push_back(ret);
+ g_dstates.modify([ret](servers_t& servers) {
+ servers.push_back(ret);
std::stable_sort(servers.begin(), servers.end(), [](const decltype(ret)& a, const decltype(ret)& b) {
return a->order < b->order;
});
return ret;
} );
- g_lua.writeFunction("makeRule", makeRule);
-
- g_lua.writeFunction("addAnyTCRule", []() {
- setLuaSideEffect();
- warnlog("addAnyTCRule() is deprecated and will be removed in 1.3.0, please use addAction(AndRule{QTypeRule(dnsdist.ANY), TCPRule(false)}, TCAction()) instead");
-
- auto rules=g_rulactions.getCopy();
- std::vector<pair<int, shared_ptr<DNSRule> >> v;
- v.push_back({1, std::make_shared<QTypeRule>(0xff)});
- v.push_back({2, std::make_shared<TCPRule>(false)});
- rules.push_back({ std::shared_ptr<DNSRule>(new AndRule(v)), std::make_shared<TCAction>()});
- g_rulactions.setState(rules);
- });
-
- g_lua.writeFunction("rmRule", [](unsigned int num) {
- setLuaSideEffect();
- auto rules = g_rulactions.getCopy();
- if(num >= rules.size()) {
- g_outputBuffer = "Error: attempt to delete non-existing rule\n";
- return;
- }
- rules.erase(rules.begin()+num);
- g_rulactions.setState(rules);
- });
-
- g_lua.writeFunction("topRule", []() {
- setLuaSideEffect();
- auto rules = g_rulactions.getCopy();
- if(rules.empty())
- return;
- auto subject = *rules.rbegin();
- rules.erase(std::prev(rules.end()));
- rules.insert(rules.begin(), subject);
- g_rulactions.setState(rules);
- });
- g_lua.writeFunction("mvRule", [](unsigned int from, unsigned int to) {
- setLuaSideEffect();
- auto rules = g_rulactions.getCopy();
- if(from >= rules.size() || to > rules.size()) {
- g_outputBuffer = "Error: attempt to move rules from/to invalid index\n";
- return;
- }
-
- auto subject = rules[from];
- rules.erase(rules.begin()+from);
- if(to == rules.size())
- rules.push_back(subject);
- else {
- if(from < to)
- --to;
- rules.insert(rules.begin()+to, subject);
- }
- g_rulactions.setState(rules);
- });
- g_lua.writeFunction("clearRules", []() {
- setLuaSideEffect();
- g_rulactions.modify([](decltype(g_rulactions)::value_type& rulactions) {
- rulactions.clear();
- });
- });
-
- g_lua.writeFunction("newRuleAction", [](luadnsrule_t dnsrule, std::shared_ptr<DNSAction> action) {
- auto rule=makeRule(dnsrule);
- return std::make_shared<std::pair< luadnsrule_t, std::shared_ptr<DNSAction> > >(rule, action);
- });
-
- g_lua.writeFunction("setRules", [](std::vector< std::pair<int, std::shared_ptr<std::pair<luadnsrule_t, std::shared_ptr<DNSAction> > > > > newruleactions) {
- setLuaSideEffect();
- g_rulactions.modify([newruleactions](decltype(g_rulactions)::value_type& gruleactions) {
- gruleactions.clear();
- for (const auto& newruleaction : newruleactions) {
- if (newruleaction.second) {
- auto rule=makeRule(newruleaction.second->first);
- gruleactions.push_back({rule, newruleaction.second->second});
- }
- }
- });
- });
-
- g_lua.writeFunction("rmServer",
+ g_lua.writeFunction("rmServer",
[](boost::variant<std::shared_ptr<DownstreamState>, int> var)
- {
+ {
setLuaSideEffect();
shared_ptr<DownstreamState> server;
auto* rem = boost::get<shared_ptr<DownstreamState>>(&var);
g_dstates.setState(states);
} );
-
g_lua.writeFunction("setServerPolicy", [](ServerPolicy policy) {
setLuaSideEffect();
g_policy.setState(policy);
g_lua.writeFunction("truncateTC", [](bool tc) { setLuaSideEffect(); g_truncateTC=tc; });
g_lua.writeFunction("fixupCase", [](bool fu) { setLuaSideEffect(); g_fixupCase=fu; });
- g_lua.registerMember("name", &ServerPolicy::name);
- g_lua.registerMember("policy", &ServerPolicy::policy);
- g_lua.writeFunction("newServerPolicy", [](string name, policyfunc_t policy) { return ServerPolicy{name, policy};});
- g_lua.writeVariable("firstAvailable", ServerPolicy{"firstAvailable", firstAvailable});
- g_lua.writeVariable("roundrobin", ServerPolicy{"roundrobin", roundrobin});
- g_lua.writeVariable("wrandom", ServerPolicy{"wrandom", wrandom});
- g_lua.writeVariable("whashed", ServerPolicy{"whashed", whashed});
- g_lua.writeVariable("leastOutstanding", ServerPolicy{"leastOutstanding", leastOutstanding});
g_lua.writeFunction("addACL", [](const std::string& domain) {
setLuaSideEffect();
g_ACL.modify([domain](NetmaskGroup& nmg) { nmg.addMask(domain); });
g_outputBuffer="Error: "+string(e.what())+"\n";
}
});
+
g_lua.writeFunction("setACL", [](boost::variant<string,vector<pair<int, string>>> inp) {
setLuaSideEffect();
NetmaskGroup nmg;
}
g_ACL.setState(nmg);
});
+
g_lua.writeFunction("showACL", []() {
setLuaNoSideEffect();
vector<string> vec;
g_outputBuffer+=s+"\n";
});
+
g_lua.writeFunction("shutdown", []() {
#ifdef HAVE_SYSTEMD
sd_notify(0, "STOPPING=1");
_exit(0);
} );
-
- g_lua.writeFunction("addDomainBlock", [](const std::string& domain) {
- setLuaSideEffect();
- warnlog("addDomainBlock() is deprecated and will be removed in 1.3.0, please use addAction(\"%s\", DropAction()) instead", domain);
- SuffixMatchNode smn;
- smn.add(DNSName(domain));
- g_rulactions.modify([smn](decltype(g_rulactions)::value_type& rulactions) {
- rulactions.push_back({
- std::make_shared<SuffixMatchNodeRule>(smn),
- std::make_shared<DropAction>() });
- });
-
- });
- g_lua.writeFunction("showServers", []() {
+ g_lua.writeFunction("showServers", []() {
setLuaNoSideEffect();
try {
ostringstream ret;
}
ret << (fmt % counter % s->name % s->remote.toStringWithPort() %
- status %
+ status %
s->queryLoad % s->qps.getRate() % s->order % s->weight % s->queries.load() % s->reuseds.load() % (s->dropRate) % (s->latencyUsec/1000.0) % s->outstanding.load() % pools) << endl;
totQPS += s->queryLoad;
++counter;
}
ret<< (fmt % "All" % "" % "" % ""
- %
+ %
(double)totQPS % "" % "" % "" % totQueries % totDrops % "" % "" % "" % "" ) << endl;
g_outputBuffer=ret.str();
}catch(std::exception& e) { g_outputBuffer=e.what(); throw; }
});
- g_lua.writeFunction("addLuaAction", [](luadnsrule_t var, LuaAction::func_t func)
- {
- setLuaSideEffect();
- auto rule=makeRule(var);
- g_rulactions.modify([rule,func](decltype(g_rulactions)::value_type& rulactions){
- rulactions.push_back({rule,
- std::make_shared<LuaAction>(func)});
- });
- });
-
- g_lua.writeFunction("LuaAction", [](LuaAction::func_t func) {
- setLuaSideEffect();
- return std::shared_ptr<DNSAction>(new LuaAction(func));
- });
-
- g_lua.writeFunction("addLuaResponseAction", [](luadnsrule_t var, LuaResponseAction::func_t func) {
- setLuaSideEffect();
- auto rule=makeRule(var);
- g_resprulactions.modify([rule,func](decltype(g_resprulactions)::value_type& rulactions){
- rulactions.push_back({rule,
- std::make_shared<LuaResponseAction>(func)});
- });
- });
-
- g_lua.writeFunction("LuaResponseAction", [](LuaResponseAction::func_t func) {
- setLuaSideEffect();
- return std::shared_ptr<DNSResponseAction>(new LuaResponseAction(func));
- });
-
- g_lua.writeFunction("NoRecurseAction", []() {
- return std::shared_ptr<DNSAction>(new NoRecurseAction);
- });
-
- g_lua.writeFunction("MacAddrAction", [](int code) {
- return std::shared_ptr<DNSAction>(new MacAddrAction(code));
- });
-
-
- g_lua.writeFunction("PoolAction", [](const string& a) {
- return std::shared_ptr<DNSAction>(new PoolAction(a));
- });
-
- g_lua.writeFunction("QPSAction", [](int limit) {
- return std::shared_ptr<DNSAction>(new QPSAction(limit));
+ g_lua.writeFunction("getServers", []() {
+ setLuaNoSideEffect();
+ vector<pair<int, std::shared_ptr<DownstreamState> > > ret;
+ int count=1;
+ for(const auto& s : g_dstates.getCopy()) {
+ ret.push_back(make_pair(count++, s));
+ }
+ return ret;
});
- g_lua.writeFunction("QPSPoolAction", [](int limit, const string& a) {
- return std::shared_ptr<DNSAction>(new QPSPoolAction(limit, a));
+ g_lua.writeFunction("getPoolServers", [](string pool) {
+ return getDownstreamCandidates(g_pools.getCopy(), pool);
});
- g_lua.writeFunction("SpoofAction", [](boost::variant<string,vector<pair<int, string>>> inp, boost::optional<string> b ) {
- vector<ComboAddress> addrs;
- if(auto s = boost::get<string>(&inp))
- addrs.push_back(ComboAddress(*s));
- else {
- const auto& v = boost::get<vector<pair<int,string>>>(inp);
- for(const auto& a: v)
- addrs.push_back(ComboAddress(a.second));
- }
- if(b)
- addrs.push_back(ComboAddress(*b));
- return std::shared_ptr<DNSAction>(new SpoofAction(addrs));
+ g_lua.writeFunction("getServer", [client](int i) {
+ if (client)
+ return std::make_shared<DownstreamState>(ComboAddress());
+ return g_dstates.getCopy().at(i);
});
- g_lua.writeFunction("SpoofCNAMEAction", [](const string& a) {
- return std::shared_ptr<DNSAction>(new SpoofAction(a));
- });
+ g_lua.writeFunction("carbonServer", [](const std::string& address, boost::optional<string> ourName,
+ boost::optional<unsigned int> interval) {
+ setLuaSideEffect();
+ auto ours = g_carbon.getCopy();
+ ours.push_back({ComboAddress(address, 2003), ourName ? *ourName : "", interval ? *interval : 30});
+ g_carbon.setState(ours);
+ });
- g_lua.writeFunction("addDomainSpoof", [](const std::string& domain, boost::variant<string,vector<pair<int, string>>> inp, boost::optional<string> b) {
+ g_lua.writeFunction("webserver", [client](const std::string& address, const std::string& password, const boost::optional<std::string> apiKey, const boost::optional<std::map<std::string, std::string> > customHeaders) {
setLuaSideEffect();
- warnlog("addDomainSpoof() is deprecated and will be removed in 1.3.0, please use addAction(\"%s\", SpoofAction(...)) instead", domain);
-
- SuffixMatchNode smn;
- vector<ComboAddress> outp;
- try
- {
- smn.add(DNSName(domain));
-
- if(auto s = boost::get<string>(&inp))
- outp.push_back(ComboAddress(*s));
- else {
- const auto& v = boost::get<vector<pair<int,string>>>(inp);
- for(const auto& a: v)
- outp.push_back(ComboAddress(a.second));
- }
- if(b)
- outp.push_back(ComboAddress(*b));
-
+ if(client)
+ return;
+ ComboAddress local(address);
+ try {
+ int sock = SSocket(local.sin4.sin_family, SOCK_STREAM, 0);
+ SSetsockopt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
+ SBind(sock, local);
+ SListen(sock, 5);
+ auto launch=[sock, local, password, apiKey, customHeaders]() {
+ thread t(dnsdistWebserverThread, sock, local, password, apiKey ? *apiKey : "", customHeaders);
+ t.detach();
+ };
+ if(g_launchWork)
+ g_launchWork->push_back(launch);
+ else
+ launch();
}
catch(std::exception& e) {
- g_outputBuffer="Error parsing parameters: "+string(e.what());
- return;
+ g_outputBuffer="Unable to bind to webserver socket on " + local.toStringWithPort() + ": " + e.what();
+ errlog("Unable to bind to webserver socket on %s: %s", local.toStringWithPort(), e.what());
}
- g_rulactions.modify([&smn,&outp](decltype(g_rulactions)::value_type& rulactions) {
- rulactions.push_back({
- std::make_shared<SuffixMatchNodeRule>(smn),
- std::make_shared<SpoofAction>(outp) });
- });
});
- g_lua.writeFunction("addDomainCNAMESpoof", [](const std::string& domain, const std::string& cname) {
+ g_lua.writeFunction("controlSocket", [client](const std::string& str) {
setLuaSideEffect();
- warnlog("addDomainCNAMESpoof() is deprecated and will be removed in 1.3.0, please use addAction(\"%s\", SpoofCNAMEAction(\"%s\")) instead", domain, cname);
+ ComboAddress local(str, 5199);
- SuffixMatchNode smn;
- try
- {
- smn.add(DNSName(domain));
- }
- catch(std::exception& e) {
- g_outputBuffer="Error parsing parameters: "+string(e.what());
+ if(client) {
+ g_serverControl = local;
return;
}
- g_rulactions.modify([&smn,&cname](decltype(g_rulactions)::value_type& rulactions) {
- rulactions.push_back({
- std::make_shared<SuffixMatchNodeRule>(smn),
- std::make_shared<SpoofAction>(cname) });
- });
- });
- g_lua.writeFunction("DropAction", []() {
- return std::shared_ptr<DNSAction>(new DropAction);
- });
+ try {
+ int sock = SSocket(local.sin4.sin_family, SOCK_STREAM, 0);
+ SSetsockopt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
+ SBind(sock, local);
+ SListen(sock, 5);
+ auto launch=[sock, local]() {
+ thread t(controlThread, sock, local);
+ t.detach();
+ };
+ if(g_launchWork)
+ g_launchWork->push_back(launch);
+ else
+ launch();
- g_lua.writeFunction("AllowAction", []() {
- return std::shared_ptr<DNSAction>(new AllowAction);
+ }
+ catch(std::exception& e) {
+ g_outputBuffer="Unable to bind to control socket on " + local.toStringWithPort() + ": " + e.what();
+ errlog("Unable to bind to control socket on %s: %s", local.toStringWithPort(), e.what());
+ }
});
- g_lua.writeFunction("DelayAction", [](int msec) {
- return std::shared_ptr<DNSAction>(new DelayAction(msec));
- });
+ g_lua.writeFunction("clearQueryCounters", []() {
+ unsigned int size{0};
+ {
+ WriteLock wl(&g_qcount.queryLock);
+ size = g_qcount.records.size();
+ g_qcount.records.clear();
+ }
- g_lua.writeFunction("TCAction", []() {
- return std::shared_ptr<DNSAction>(new TCAction);
+ boost::format fmt("%d records cleared from query counter buffer\n");
+ g_outputBuffer = (fmt % size).str();
});
- g_lua.writeFunction("DisableValidationAction", []() {
- return std::shared_ptr<DNSAction>(new DisableValidationAction);
- });
+ g_lua.writeFunction("getQueryCounters", [](boost::optional<unsigned int> optMax) {
+ setLuaNoSideEffect();
+ ReadLock rl(&g_qcount.queryLock);
+ g_outputBuffer = "query counting is currently: ";
+ g_outputBuffer+= g_qcount.enabled ? "enabled" : "disabled";
+ g_outputBuffer+= (boost::format(" (%d records in buffer)\n") % g_qcount.records.size()).str();
- g_lua.writeFunction("LogAction", [](const std::string& fname, boost::optional<bool> binary, boost::optional<bool> append, boost::optional<bool> buffered) {
- return std::shared_ptr<DNSAction>(new LogAction(fname, binary ? *binary : true, append ? *append : false, buffered ? *buffered : false));
+ boost::format fmt("%-3d %s: %d request(s)\n");
+ QueryCountRecords::iterator it;
+ unsigned int max = optMax ? *optMax : 10;
+ unsigned int index{1};
+ for(it = g_qcount.records.begin(); it != g_qcount.records.end() && index <= max; ++it, ++index) {
+ g_outputBuffer += (fmt % index % it->first % it->second).str();
+ }
});
- g_lua.writeFunction("RCodeAction", [](int rcode) {
- return std::shared_ptr<DNSAction>(new RCodeAction(rcode));
- });
+ g_lua.writeFunction("setQueryCount", [](bool enabled) { g_qcount.enabled=enabled; });
- g_lua.writeFunction("SkipCacheAction", []() {
- return std::shared_ptr<DNSAction>(new SkipCacheAction);
+ g_lua.writeFunction("setQueryCountFilter", [](QueryCountFilter func) {
+ g_qcount.filter = func;
});
- g_lua.writeFunction("MaxQPSIPRule", [](unsigned int qps, boost::optional<int> ipv4trunc, boost::optional<int> ipv6trunc, boost::optional<int> burst) {
- return std::shared_ptr<DNSRule>(new MaxQPSIPRule(qps, burst.get_value_or(qps), ipv4trunc.get_value_or(32), ipv6trunc.get_value_or(64)));
+ g_lua.writeFunction("makeKey", []() {
+ setLuaNoSideEffect();
+ g_outputBuffer="setKey("+newKey()+")\n";
});
+ g_lua.writeFunction("setKey", [](const std::string& key) {
+ if(!g_configurationDone && ! g_key.empty()) { // this makes sure the commandline -k key prevails over dnsdist.conf
+ return; // but later setKeys() trump the -k value again
+ }
- g_lua.writeFunction("MaxQPSRule", [](unsigned int qps, boost::optional<int> burst) {
- if(!burst)
- return std::shared_ptr<DNSRule>(new MaxQPSRule(qps));
+ setLuaSideEffect();
+ string newkey;
+ if(B64Decode(key, newkey) < 0) {
+ g_outputBuffer=string("Unable to decode ")+key+" as Base64";
+ errlog("%s", g_outputBuffer);
+ }
else
- return std::shared_ptr<DNSRule>(new MaxQPSRule(qps, *burst));
+ g_key=newkey;
});
+ g_lua.writeFunction("testCrypto", [](boost::optional<string> optTestMsg)
+ {
+ setLuaNoSideEffect();
+#ifdef HAVE_LIBSODIUM
+ try {
+ string testmsg;
- g_lua.writeFunction("RegexRule", [](const std::string& str) {
- return std::shared_ptr<DNSRule>(new RegexRule(str));
- });
+ if (optTestMsg) {
+ testmsg = *optTestMsg;
+ }
+ else {
+ testmsg = "testStringForCryptoTests";
+ }
-#ifdef HAVE_RE2
- g_lua.writeFunction("RE2Rule", [](const std::string& str) {
- return std::shared_ptr<DNSRule>(new RE2Rule(str));
- });
-#endif
+ SodiumNonce sn, sn2;
+ sn.init();
+ sn2=sn;
+ string encrypted = sodEncryptSym(testmsg, g_key, sn);
+ string decrypted = sodDecryptSym(encrypted, g_key, sn2);
- g_lua.writeFunction("SuffixMatchNodeRule", [](const SuffixMatchNode& smn, boost::optional<bool> quiet) {
- return std::shared_ptr<DNSRule>(new SuffixMatchNodeRule(smn, quiet ? *quiet : false));
- });
+ sn.increment();
+ sn2.increment();
- g_lua.writeFunction("NetmaskGroupRule", [](const NetmaskGroup& nmg, boost::optional<bool> src) {
- return std::shared_ptr<DNSRule>(new NetmaskGroupRule(nmg, src ? *src : true));
- });
+ encrypted = sodEncryptSym(testmsg, g_key, sn);
+ decrypted = sodDecryptSym(encrypted, g_key, sn2);
- g_lua.writeFunction("benchRule", [](std::shared_ptr<DNSRule> rule, boost::optional<int> times_, boost::optional<string> suffix_) {
- setLuaNoSideEffect();
- int times = times_.get_value_or(100000);
- DNSName suffix(suffix_.get_value_or("powerdns.com"));
- struct item {
- vector<uint8_t> packet;
- ComboAddress rem;
- DNSName qname;
- uint16_t qtype, qclass;
- };
- vector<item> items;
- items.reserve(1000);
- for(int n=0; n < 1000; ++n) {
- struct item i;
- i.qname=DNSName(std::to_string(random()));
- i.qname += suffix;
- i.qtype = random() % 0xff;
- i.qclass = 1;
- i.rem=ComboAddress("127.0.0.1");
- i.rem.sin4.sin_addr.s_addr = random();
- DNSPacketWriter pw(i.packet, i.qname, i.qtype);
- items.push_back(i);
- }
+ if(testmsg == decrypted)
+ g_outputBuffer="Everything is ok!\n";
+ else
+ g_outputBuffer="Crypto failed..\n";
- int matches=0;
- ComboAddress dummy("127.0.0.1");
- DTime dt;
- dt.set();
- for(int n=0; n < times; ++n) {
- const item& i = items[n % items.size()];
- DNSQuestion dq(&i.qname, i.qtype, i.qclass, &i.rem, &i.rem, (struct dnsheader*)&i.packet[0], i.packet.size(), i.packet.size(), false);
- if(rule->matches(&dq))
- matches++;
- }
- double udiff=dt.udiff();
- g_outputBuffer=(boost::format("Had %d matches out of %d, %.1f qps, in %.1f usec\n") % matches % times % (1000000*(1.0*times/udiff)) % udiff).str();
+ }
+ catch(...) {
+ g_outputBuffer="Crypto failed..\n";
+ }
+#else
+ g_outputBuffer="Crypto not available.\n";
+#endif
+ });
- });
+ g_lua.writeFunction("setTCPRecvTimeout", [](int timeout) { g_tcpRecvTimeout=timeout; });
- g_lua.writeFunction("AllRule", []() {
- return std::shared_ptr<DNSRule>(new AllRule());
- });
+ g_lua.writeFunction("setTCPSendTimeout", [](int timeout) { g_tcpSendTimeout=timeout; });
- g_lua.writeFunction("ProbaRule", [](double proba) {
- return std::shared_ptr<DNSRule>(new ProbaRule(proba));
- });
+ g_lua.writeFunction("setUDPTimeout", [](int timeout) { g_udpTimeout=timeout; });
-
- g_lua.writeFunction("QNameRule", [](const std::string& qname) {
- return std::shared_ptr<DNSRule>(new QNameRule(DNSName(qname)));
- });
-
- g_lua.writeFunction("QTypeRule", [](boost::variant<int, std::string> str) {
- uint16_t qtype;
- if(auto dir = boost::get<int>(&str)) {
- qtype = *dir;
- }
- else {
- string val=boost::get<string>(str);
- qtype = QType::chartocode(val.c_str());
- if(!qtype)
- throw std::runtime_error("Unable to convert '"+val+"' to a DNS type");
+ g_lua.writeFunction("setMaxUDPOutstanding", [](uint16_t max) {
+ if (!g_configurationDone) {
+ g_maxOutstanding = max;
+ } else {
+ g_outputBuffer="Max UDP outstanding cannot be altered at runtime!\n";
}
- return std::shared_ptr<DNSRule>(new QTypeRule(qtype));
- });
- g_lua.writeFunction("QClassRule", [](int c) {
- return std::shared_ptr<DNSRule>(new QClassRule(c));
- });
-
- g_lua.writeFunction("OpcodeRule", [](uint8_t code) {
- return std::shared_ptr<DNSRule>(new OpcodeRule(code));
- });
-
- g_lua.writeFunction("AndRule", [](vector<pair<int, std::shared_ptr<DNSRule> > >a) {
- return std::shared_ptr<DNSRule>(new AndRule(a));
- });
-
- g_lua.writeFunction("OrRule", [](vector<pair<int, std::shared_ptr<DNSRule> > >a) {
- return std::shared_ptr<DNSRule>(new OrRule(a));
- });
-
- g_lua.writeFunction("TCPRule", [](bool tcp) {
- return std::shared_ptr<DNSRule>(new TCPRule(tcp));
- });
-
- g_lua.writeFunction("DNSSECRule", []() {
- return std::shared_ptr<DNSRule>(new DNSSECRule());
- });
-
- g_lua.writeFunction("NotRule", [](std::shared_ptr<DNSRule>rule) {
- return std::shared_ptr<DNSRule>(new NotRule(rule));
- });
-
- g_lua.writeFunction("RecordsCountRule", [](uint8_t section, uint16_t minCount, uint16_t maxCount) {
- return std::shared_ptr<DNSRule>(new RecordsCountRule(section, minCount, maxCount));
});
- g_lua.writeFunction("RecordsTypeCountRule", [](uint8_t section, uint16_t type, uint16_t minCount, uint16_t maxCount) {
- return std::shared_ptr<DNSRule>(new RecordsTypeCountRule(section, type, minCount, maxCount));
+ g_lua.writeFunction("setMaxTCPClientThreads", [](uint64_t max) {
+ if (!g_configurationDone) {
+ g_maxTCPClientThreads = max;
+ } else {
+ g_outputBuffer="Maximum TCP client threads count cannot be altered at runtime!\n";
+ }
});
- g_lua.writeFunction("TrailingDataRule", []() {
- return std::shared_ptr<DNSRule>(new TrailingDataRule());
+ g_lua.writeFunction("setMaxTCPQueuedConnections", [](uint64_t max) {
+ if (!g_configurationDone) {
+ g_maxTCPQueuedConnections = max;
+ } else {
+ g_outputBuffer="The maximum number of queued TCP connections cannot be altered at runtime!\n";
+ }
});
- g_lua.writeFunction("QNameLabelsCountRule", [](unsigned int minLabelsCount, unsigned int maxLabelsCount) {
- return std::shared_ptr<DNSRule>(new QNameLabelsCountRule(minLabelsCount, maxLabelsCount));
+ g_lua.writeFunction("setMaxTCPQueriesPerConnection", [](size_t max) {
+ if (!g_configurationDone) {
+ g_maxTCPQueriesPerConn = max;
+ } else {
+ g_outputBuffer="The maximum number of queries per TCP connection cannot be altered at runtime!\n";
+ }
});
- g_lua.writeFunction("QNameWireLengthRule", [](size_t min, size_t max) {
- return std::shared_ptr<DNSRule>(new QNameWireLengthRule(min, max));
+ g_lua.writeFunction("setMaxTCPConnectionsPerClient", [](size_t max) {
+ if (!g_configurationDone) {
+ g_maxTCPConnectionsPerClient = max;
+ } else {
+ g_outputBuffer="The maximum number of TCP connection per client cannot be altered at runtime!\n";
+ }
});
- g_lua.writeFunction("RCodeRule", [](int rcode) {
- return std::shared_ptr<DNSRule>(new RCodeRule(rcode));
+ g_lua.writeFunction("setMaxTCPConnectionDuration", [](size_t max) {
+ if (!g_configurationDone) {
+ g_maxTCPConnectionDuration = max;
+ } else {
+ g_outputBuffer="The maximum duration of a TCP connection cannot be altered at runtime!\n";
+ }
});
- g_lua.writeFunction("addAction", [](luadnsrule_t var, boost::variant<std::shared_ptr<DNSAction>, std::shared_ptr<DNSResponseAction> > era)
- {
- if (era.type() == typeid(std::shared_ptr<DNSResponseAction>)) {
- throw std::runtime_error("addAction() can only be called with query-related actions, not response-related ones. Are you looking for addResponseAction()?");
- }
-
- auto ea = *boost::get<std::shared_ptr<DNSAction>>(&era);
- setLuaSideEffect();
- auto rule=makeRule(var);
- g_rulactions.modify([rule, ea](decltype(g_rulactions)::value_type& rulactions){
- rulactions.push_back({rule, ea});
- });
- });
-
-
- g_lua.writeFunction("addPoolRule", [](luadnsrule_t var, string pool) {
- setLuaSideEffect();
- warnlog("addPoolRule() is deprecated and will be removed in 1.3.0, please use addAction(..., PoolAction(\"%s\")) instead", pool);
-
- auto rule=makeRule(var);
- g_rulactions.modify([rule, pool](decltype(g_rulactions)::value_type& rulactions) {
- rulactions.push_back({
- rule,
- std::make_shared<PoolAction>(pool) });
- });
- });
+ g_lua.writeFunction("setCacheCleaningDelay", [](uint32_t delay) { g_cacheCleaningDelay = delay; });
- g_lua.writeFunction("addNoRecurseRule", [](luadnsrule_t var) {
- setLuaSideEffect();
- warnlog("addNoRecurseRule() is deprecated and will be removed in 1.3.0, please use addAction(..., NoRecurseAction()) instead");
-
- auto rule=makeRule(var);
- g_rulactions.modify([rule](decltype(g_rulactions)::value_type& rulactions) {
- rulactions.push_back({
- rule,
- std::make_shared<NoRecurseAction>() });
- });
- });
+ g_lua.writeFunction("setCacheCleaningPercentage", [](uint16_t percentage) { if (percentage < 100) g_cacheCleaningPercentage = percentage; else g_cacheCleaningPercentage = 100; });
- g_lua.writeFunction("addDisableValidationRule", [](luadnsrule_t var) {
- setLuaSideEffect();
- warnlog("addDisableValidationRule() is deprecated and will be removed in 1.3.0, please use addAction(..., DisableValidationAction()) instead");
-
- auto rule=makeRule(var);
- g_rulactions.modify([rule](decltype(g_rulactions)::value_type& rulactions) {
- rulactions.push_back({
- rule,
- std::make_shared<DisableValidationAction>() });
- });
- });
+ g_lua.writeFunction("setECSSourcePrefixV4", [](uint16_t prefix) { g_ECSSourcePrefixV4=prefix; });
+ g_lua.writeFunction("setECSSourcePrefixV6", [](uint16_t prefix) { g_ECSSourcePrefixV6=prefix; });
- g_lua.writeFunction("addQPSPoolRule", [](luadnsrule_t var, int limit, string pool) {
- setLuaSideEffect();
- warnlog("addQPSPoolRule() is deprecated and will be removed in 1.3.0, please use addAction(..., QPSPoolAction(%d, \"%s\")) instead", limit, pool);
-
- auto rule = makeRule(var);
- g_rulactions.modify([rule, pool,limit](decltype(g_rulactions)::value_type& rulactions) {
- rulactions.push_back({
- rule,
- std::make_shared<QPSPoolAction>(limit, pool) });
- });
- });
+ g_lua.writeFunction("setECSOverride", [](bool override) { g_ECSOverride=override; });
- g_lua.writeFunction("setDNSSECPool", [](const std::string& pool) {
- setLuaSideEffect();
- warnlog("setDNSSECPool() is deprecated and will be removed in 1.3.0, please use addAction(DNSSECRule(), PoolAction(\"%s\")) instead", pool);
+ g_lua.writeFunction("showDynBlocks", []() {
+ setLuaNoSideEffect();
+ auto slow = g_dynblockNMG.getCopy();
+ struct timespec now;
+ gettime(&now);
+ boost::format fmt("%-24s %8d %8d %s\n");
+ g_outputBuffer = (fmt % "What" % "Seconds" % "Blocks" % "Reason").str();
+ for(const auto& e: slow) {
+ if(now < e->second.until)
+ g_outputBuffer+= (fmt % e->first.toString() % (e->second.until.tv_sec - now.tv_sec) % e->second.blocks % e->second.reason).str();
+ }
+ auto slow2 = g_dynblockSMT.getCopy();
+ slow2.visit([&now, &fmt](const SuffixMatchTree<DynBlock>& node) {
+ if(now <node.d_value.until) {
+ string dom("empty");
+ if(!node.d_value.domain.empty())
+ dom = node.d_value.domain.toString();
+ g_outputBuffer+= (fmt % dom % (node.d_value.until.tv_sec - now.tv_sec) % node.d_value.blocks % node.d_value.reason).str();
+ }
+ });
- g_rulactions.modify([pool](decltype(g_rulactions)::value_type& rulactions) {
- rulactions.push_back({std::make_shared<DNSSECRule>(),
- std::make_shared<PoolAction>(pool)});
- });
});
- g_lua.writeFunction("addQPSLimit", [](luadnsrule_t var, int lim) {
+ g_lua.writeFunction("clearDynBlocks", []() {
setLuaSideEffect();
- warnlog("addQPSLimit() is deprecated and will be removed in 1.3.0, please use addAction(..., QPSAction(%d)) instead", lim);
-
- auto rule = makeRule(var);
- g_rulactions.modify([lim,rule](decltype(g_rulactions)::value_type& rulactions) {
- rulactions.push_back({rule,
- std::make_shared<QPSAction>(lim)});
- });
+ nmts_t nmg;
+ g_dynblockNMG.setState(nmg);
+ SuffixMatchTree<DynBlock> smt;
+ g_dynblockSMT.setState(smt);
+ });
+
+ g_lua.writeFunction("addDynBlocks",
+ [](const map<ComboAddress,int>& m, const std::string& msg, boost::optional<int> seconds, boost::optional<DNSAction::Action> action) {
+ setLuaSideEffect();
+ auto slow = g_dynblockNMG.getCopy();
+ struct timespec until, now;
+ gettime(&now);
+ until=now;
+ int actualSeconds = seconds ? *seconds : 10;
+ until.tv_sec += actualSeconds;
+ for(const auto& capair : m) {
+ unsigned int count = 0;
+ auto got = slow.lookup(Netmask(capair.first));
+ bool expired=false;
+ if(got) {
+ if(until < got->second.until) // had a longer policy
+ continue;
+ if(now < got->second.until) // only inherit count on fresh query we are extending
+ count=got->second.blocks;
+ else
+ expired=true;
+ }
+ DynBlock db{msg,until,DNSName(),(action ? *action : DNSAction::Action::None)};
+ db.blocks=count;
+ if(!got || expired)
+ warnlog("Inserting dynamic block for %s for %d seconds: %s", capair.first.toString(), actualSeconds, msg);
+ slow.insert(Netmask(capair.first)).second=db;
+ }
+ g_dynblockNMG.setState(slow);
+ });
+
+ g_lua.writeFunction("addDynBlockSMT",
+ [](const vector<pair<unsigned int, string> >&names, const std::string& msg, boost::optional<int> seconds, boost::optional<DNSAction::Action> action) {
+ setLuaSideEffect();
+ auto slow = g_dynblockSMT.getCopy();
+ struct timespec until, now;
+ gettime(&now);
+ until=now;
+ int actualSeconds = seconds ? *seconds : 10;
+ until.tv_sec += actualSeconds;
+
+ for(const auto& capair : names) {
+ unsigned int count = 0;
+ DNSName domain(capair.second);
+ auto got = slow.lookup(domain);
+ bool expired=false;
+ if(got) {
+ if(until < got->until) // had a longer policy
+ continue;
+ if(now < got->until) // only inherit count on fresh query we are extending
+ count=got->blocks;
+ else
+ expired=true;
+ }
+
+ DynBlock db{msg,until,domain,(action ? *action : DNSAction::Action::None)};
+ db.blocks=count;
+ if(!got || expired)
+ warnlog("Inserting dynamic block for %s for %d seconds: %s", domain, actualSeconds, msg);
+ slow.add(domain, db);
+ }
+ g_dynblockSMT.setState(slow);
+ });
+
+ g_lua.writeFunction("setDynBlocksAction", [](DNSAction::Action action) {
+ if (!g_configurationDone) {
+ if (action == DNSAction::Action::Drop || action == DNSAction::Action::Refused || action == DNSAction::Action::Truncate) {
+ g_dynBlockAction = action;
+ }
+ else {
+ errlog("Dynamic blocks action can only be Drop, Refused or Truncate!");
+ g_outputBuffer="Dynamic blocks action can only be Drop, Refused or Truncate!\n";
+ }
+ } else {
+ g_outputBuffer="Dynamic blocks action cannot be altered at runtime!\n";
+ }
});
- g_lua.writeFunction("addDelay", [](luadnsrule_t var, int msec) {
- setLuaSideEffect();
- warnlog("addDelay() is deprecated and will be removed in 1.3.0, please use addAction(..., DelayAction(%d)) instead", msec);
-
- auto rule = makeRule(var);
- g_rulactions.modify([msec,rule](decltype(g_rulactions)::value_type& rulactions) {
- rulactions.push_back({rule,
- std::make_shared<DelayAction>(msec)});
- });
- });
+ g_lua.writeFunction("addDNSCryptBind", [](const std::string& addr, const std::string& providerName, const std::string& certFile, const std::string keyFile, boost::optional<localbind_t> vars) {
+ if (g_configurationDone) {
+ g_outputBuffer="addDNSCryptBind cannot be used at runtime!\n";
+ return;
+ }
+#ifdef HAVE_DNSCRYPT
+ bool doTCP = true;
+ bool reusePort = false;
+ int tcpFastOpenQueueSize = 0;
+ std::string interface;
+ std::set<int> cpus;
+ parseLocalBindVars(vars, doTCP, reusePort, tcpFastOpenQueueSize, interface, cpus);
- g_lua.writeFunction("showRules", []() {
- setLuaNoSideEffect();
- boost::format fmt("%-3d %9d %-50s %s\n");
- g_outputBuffer += (fmt % "#" % "Matches" % "Rule" % "Action").str();
- int num=0;
- for(const auto& lim : g_rulactions.getCopy()) {
- string name = lim.first->toString();
- g_outputBuffer += (fmt % num % lim.first->d_matches % name % lim.second->toString()).str();
- ++num;
+ try {
+ DnsCryptContext ctx(providerName, certFile, keyFile);
+ g_dnsCryptLocals.push_back(std::make_tuple(ComboAddress(addr, 443), ctx, reusePort, tcpFastOpenQueueSize, interface, cpus));
}
+ catch(std::exception& e) {
+ errlog(e.what());
+ g_outputBuffer="Error: "+string(e.what())+"\n";
+ }
+#else
+ g_outputBuffer="Error: DNSCrypt support is not enabled.\n";
+#endif
});
- g_lua.writeFunction("getServers", []() {
+ g_lua.writeFunction("showDNSCryptBinds", []() {
setLuaNoSideEffect();
- vector<pair<int, std::shared_ptr<DownstreamState> > > ret;
- int count=1;
- for(const auto& s : g_dstates.getCopy()) {
- ret.push_back(make_pair(count++, s));
+#ifdef HAVE_DNSCRYPT
+ ostringstream ret;
+ boost::format fmt("%1$-3d %2% %|25t|%3$-20.20s %|26t|%4$-8d %|35t|%5$-21.21s %|56t|%6$-9d %|66t|%7$-21.21s" );
+ ret << (fmt % "#" % "Address" % "Provider Name" % "Serial" % "Validity" % "P. Serial" % "P. Validity") << endl;
+ size_t idx = 0;
+
+ for (const auto& local : g_dnsCryptLocals) {
+ const DnsCryptContext& ctx = std::get<1>(local);
+ bool const hasOldCert = ctx.hasOldCertificate();
+ const DnsCryptCert& cert = ctx.getCurrentCertificate();
+ const DnsCryptCert& oldCert = ctx.getOldCertificate();
+
+ ret<< (fmt % idx % std::get<0>(local).toStringWithPort() % ctx.getProviderName() % cert.signedData.serial % DnsCryptContext::certificateDateToStr(cert.signedData.tsEnd) % (hasOldCert ? oldCert.signedData.serial : 0) % (hasOldCert ? DnsCryptContext::certificateDateToStr(oldCert.signedData.tsEnd) : "-")) << endl;
+ idx++;
}
- return ret;
- });
- g_lua.writeFunction("getPoolServers", [](string pool) {
- return getDownstreamCandidates(g_pools.getCopy(), pool);
- });
-
- g_lua.writeFunction("getServer", [client](int i) {
- if (client)
- return std::make_shared<DownstreamState>(ComboAddress());
- return g_dstates.getCopy().at(i);
- });
-
- g_lua.registerFunction<void(DownstreamState::*)(int)>("setQPS", [](DownstreamState& s, int lim) { s.qps = lim ? QPSLimiter(lim, lim) : QPSLimiter(); });
- g_lua.registerFunction<void(std::shared_ptr<DownstreamState>::*)(string)>("addPool", [](std::shared_ptr<DownstreamState> s, string pool) {
- auto localPools = g_pools.getCopy();
- addServerToPool(localPools, pool, s);
- g_pools.setState(localPools);
- s->pools.insert(pool);
- });
- g_lua.registerFunction<void(std::shared_ptr<DownstreamState>::*)(string)>("rmPool", [](std::shared_ptr<DownstreamState> s, string pool) {
- auto localPools = g_pools.getCopy();
- removeServerFromPool(localPools, pool, s);
- g_pools.setState(localPools);
- s->pools.erase(pool);
+ g_outputBuffer=ret.str();
+#else
+ g_outputBuffer="Error: DNSCrypt support is not enabled.\n";
+#endif
});
- g_lua.registerFunction<void(DownstreamState::*)()>("getOutstanding", [](const DownstreamState& s) { g_outputBuffer=std::to_string(s.outstanding.load()); });
-
-
- g_lua.registerFunction("isUp", &DownstreamState::isUp);
- g_lua.registerFunction("setDown", &DownstreamState::setDown);
- g_lua.registerFunction("setUp", &DownstreamState::setUp);
- g_lua.registerFunction<void(DownstreamState::*)(boost::optional<bool> newStatus)>("setAuto", [](DownstreamState& s, boost::optional<bool> newStatus) {
- if (newStatus) {
- s.upStatus = *newStatus;
+ g_lua.writeFunction("getDNSCryptBind", [client](size_t idx) {
+ setLuaNoSideEffect();
+#ifdef HAVE_DNSCRYPT
+ DnsCryptContext* ret = nullptr;
+ if (idx < g_dnsCryptLocals.size()) {
+ ret = &(std::get<1>(g_dnsCryptLocals.at(idx)));
}
- s.setAuto();
+ return ret;
+#else
+ g_outputBuffer="Error: DNSCrypt support is not enabled.\n";
+#endif
});
- g_lua.registerFunction("getName", &DownstreamState::getName);
- g_lua.registerFunction("getNameWithAddr", &DownstreamState::getNameWithAddr);
- g_lua.registerMember("upStatus", &DownstreamState::upStatus);
- g_lua.registerMember("weight", &DownstreamState::weight);
- g_lua.registerMember("order", &DownstreamState::order);
- g_lua.registerMember("name", &DownstreamState::name);
-
- g_lua.writeFunction("infolog", [](const string& arg) {
- infolog("%s", arg);
- });
- g_lua.writeFunction("errlog", [](const string& arg) {
- errlog("%s", arg);
- });
- g_lua.writeFunction("warnlog", [](const string& arg) {
- warnlog("%s", arg);
- });
+ g_lua.writeFunction("generateDNSCryptProviderKeys", [](const std::string& publicKeyFile, const std::string privateKeyFile) {
+ setLuaNoSideEffect();
+#ifdef HAVE_DNSCRYPT
+ unsigned char publicKey[DNSCRYPT_PROVIDER_PUBLIC_KEY_SIZE];
+ unsigned char privateKey[DNSCRYPT_PROVIDER_PRIVATE_KEY_SIZE];
+ sodium_mlock(privateKey, sizeof(privateKey));
+ try {
+ DnsCryptContext::generateProviderKeys(publicKey, privateKey);
- g_lua.writeFunction("show", [](const string& arg) {
- g_outputBuffer+=arg;
- g_outputBuffer+="\n";
- });
+ ofstream pubKStream(publicKeyFile);
+ pubKStream.write((char*) publicKey, sizeof(publicKey));
+ pubKStream.close();
- g_lua.registerFunction<void(dnsheader::*)(bool)>("setRD", [](dnsheader& dh, bool v) {
- dh.rd=v;
- });
+ ofstream privKStream(privateKeyFile);
+ privKStream.write((char*) privateKey, sizeof(privateKey));
+ privKStream.close();
- g_lua.registerFunction<bool(dnsheader::*)()>("getRD", [](dnsheader& dh) {
- return (bool)dh.rd;
- });
+ g_outputBuffer="Provider fingerprint is: " + DnsCryptContext::getProviderFingerprint(publicKey) + "\n";
+ }
+ catch(std::exception& e) {
+ errlog(e.what());
+ g_outputBuffer="Error: "+string(e.what())+"\n";
+ }
- g_lua.registerFunction<void(dnsheader::*)(bool)>("setCD", [](dnsheader& dh, bool v) {
- dh.cd=v;
+ sodium_memzero(privateKey, sizeof(privateKey));
+ sodium_munlock(privateKey, sizeof(privateKey));
+#else
+ g_outputBuffer="Error: DNSCrypt support is not enabled.\n";
+#endif
});
- g_lua.registerFunction<bool(dnsheader::*)()>("getCD", [](dnsheader& dh) {
- return (bool)dh.cd;
- });
+ g_lua.writeFunction("printDNSCryptProviderFingerprint", [](const std::string& publicKeyFile) {
+ setLuaNoSideEffect();
+#ifdef HAVE_DNSCRYPT
+ unsigned char publicKey[DNSCRYPT_PROVIDER_PUBLIC_KEY_SIZE];
+ try {
+ ifstream file(publicKeyFile);
+ file.read((char *) &publicKey, sizeof(publicKey));
- g_lua.registerFunction<void(dnsheader::*)(bool)>("setTC", [](dnsheader& dh, bool v) {
- dh.tc=v;
- if(v) dh.ra = dh.rd; // you'll always need this, otherwise TC=1 gets ignored
- });
+ if (file.fail())
+ throw std::runtime_error("Invalid dnscrypt provider public key file " + publicKeyFile);
- g_lua.registerFunction<void(dnsheader::*)(bool)>("setQR", [](dnsheader& dh, bool v) {
- dh.qr=v;
+ file.close();
+ g_outputBuffer="Provider fingerprint is: " + DnsCryptContext::getProviderFingerprint(publicKey) + "\n";
+ }
+ catch(std::exception& e) {
+ errlog(e.what());
+ g_outputBuffer="Error: "+string(e.what())+"\n";
+ }
+#else
+ g_outputBuffer="Error: DNSCrypt support is not enabled.\n";
+#endif
});
- g_lua.registerFunction<string(std::shared_ptr<DNSRule>::*)()>("toString", [](const std::shared_ptr<DNSRule>& rule) { return rule->toString(); });
-
- g_lua.registerFunction<string(ComboAddress::*)()>("tostring", [](const ComboAddress& ca) { return ca.toString(); });
- g_lua.registerFunction<string(ComboAddress::*)()>("tostringWithPort", [](const ComboAddress& ca) { return ca.toStringWithPort(); });
- g_lua.registerFunction<string(ComboAddress::*)()>("toString", [](const ComboAddress& ca) { return ca.toString(); });
- g_lua.registerFunction<string(ComboAddress::*)()>("toStringWithPort", [](const ComboAddress& ca) { return ca.toStringWithPort(); });
- g_lua.registerFunction<uint16_t(ComboAddress::*)()>("getPort", [](const ComboAddress& ca) { return ntohs(ca.sin4.sin_port); } );
- g_lua.registerFunction<void(ComboAddress::*)(unsigned int)>("truncate", [](ComboAddress& ca, unsigned int bits) { ca.truncate(bits); });
- g_lua.registerFunction<bool(ComboAddress::*)()>("isIPv4", [](const ComboAddress& ca) { return ca.sin4.sin_family == AF_INET; });
- g_lua.registerFunction<bool(ComboAddress::*)()>("isIPv6", [](const ComboAddress& ca) { return ca.sin4.sin_family == AF_INET6; });
- g_lua.registerFunction<bool(ComboAddress::*)()>("isMappedIPv4", [](const ComboAddress& ca) { return ca.isMappedIPv4(); });
- g_lua.registerFunction<ComboAddress(ComboAddress::*)()>("mapToIPv4", [](const ComboAddress& ca) { return ca.mapToIPv4(); });
-
- g_lua.registerFunction("isPartOf", &DNSName::isPartOf);
- g_lua.registerFunction<bool(DNSName::*)()>("chopOff", [](DNSName&dn ) { return dn.chopOff(); });
- g_lua.registerFunction<unsigned int(DNSName::*)()>("countLabels", [](const DNSName& name) { return name.countLabels(); });
- g_lua.registerFunction<size_t(DNSName::*)()>("wirelength", [](const DNSName& name) { return name.wirelength(); });
- g_lua.registerFunction<string(DNSName::*)()>("tostring", [](const DNSName&dn ) { return dn.toString(); });
- g_lua.registerFunction<string(DNSName::*)()>("toString", [](const DNSName&dn ) { return dn.toString(); });
- g_lua.writeFunction("newDNSName", [](const std::string& name) { return DNSName(name); });
- g_lua.writeFunction("newSuffixMatchNode", []() { return SuffixMatchNode(); });
-
- g_lua.registerFunction("add",(void (SuffixMatchNode::*)(const DNSName&)) &SuffixMatchNode::add);
- g_lua.registerFunction("check",(bool (SuffixMatchNode::*)(const DNSName&) const) &SuffixMatchNode::check);
-
- g_lua.writeFunction("carbonServer", [](const std::string& address, boost::optional<string> ourName,
- boost::optional<unsigned int> interval) {
- setLuaSideEffect();
- auto ours = g_carbon.getCopy();
- ours.push_back({ComboAddress(address, 2003), ourName ? *ourName : "", interval ? *interval : 30});
- g_carbon.setState(ours);
- });
+ g_lua.writeFunction("generateDNSCryptCertificate", [](const std::string& providerPrivateKeyFile, const std::string& certificateFile, const std::string privateKeyFile, uint32_t serial, time_t begin, time_t end) {
+ setLuaNoSideEffect();
+#ifdef HAVE_DNSCRYPT
+ DnsCryptPrivateKey privateKey;
+ DnsCryptCert cert;
- g_lua.writeFunction("webserver", [client](const std::string& address, const std::string& password, const boost::optional<std::string> apiKey, const boost::optional<std::map<std::string, std::string> > customHeaders) {
- setLuaSideEffect();
- if(client)
- return;
- ComboAddress local(address);
try {
- int sock = SSocket(local.sin4.sin_family, SOCK_STREAM, 0);
- SSetsockopt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
- SBind(sock, local);
- SListen(sock, 5);
- auto launch=[sock, local, password, apiKey, customHeaders]() {
- thread t(dnsdistWebserverThread, sock, local, password, apiKey ? *apiKey : "", customHeaders);
- t.detach();
- };
- if(g_launchWork)
- g_launchWork->push_back(launch);
- else
- launch();
+ if (generateDNSCryptCertificate(providerPrivateKeyFile, serial, begin, end, cert, privateKey)) {
+ privateKey.saveToFile(privateKeyFile);
+ DnsCryptContext::saveCertFromFile(cert, certificateFile);
+ }
}
- catch(std::exception& e) {
- g_outputBuffer="Unable to bind to webserver socket on " + local.toStringWithPort() + ": " + e.what();
- errlog("Unable to bind to webserver socket on %s: %s", local.toStringWithPort(), e.what());
+ catch(const std::exception& e) {
+ errlog(e.what());
+ g_outputBuffer="Error: "+string(e.what())+"\n";
}
-
+#else
+ g_outputBuffer="Error: DNSCrypt support is not enabled.\n";
+#endif
});
- g_lua.writeFunction("controlSocket", [client](const std::string& str) {
- setLuaSideEffect();
- ComboAddress local(str, 5199);
- if(client) {
- g_serverControl = local;
- return;
- }
-
+ g_lua.writeFunction("showPools", []() {
+ setLuaNoSideEffect();
try {
- int sock = SSocket(local.sin4.sin_family, SOCK_STREAM, 0);
- SSetsockopt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
- SBind(sock, local);
- SListen(sock, 5);
- auto launch=[sock, local]() {
- thread t(controlThread, sock, local);
- t.detach();
- };
- if(g_launchWork)
- g_launchWork->push_back(launch);
- else
- launch();
-
- }
- catch(std::exception& e) {
- g_outputBuffer="Unable to bind to control socket on " + local.toStringWithPort() + ": " + e.what();
- errlog("Unable to bind to control socket on %s: %s", local.toStringWithPort(), e.what());
+ ostringstream ret;
+ boost::format fmt("%1$-20.20s %|25t|%2$20s %|25t|%3$20s %|50t|%4%" );
+ // 1 2 3 4
+ ret << (fmt % "Name" % "Cache" % "ServerPolicy" % "Servers" ) << endl;
+
+ const auto localPools = g_pools.getCopy();
+ for (const auto& entry : localPools) {
+ const string& name = entry.first;
+ const std::shared_ptr<ServerPool> pool = entry.second;
+ string cache = pool->packetCache != nullptr ? pool->packetCache->toString() : "";
+ string policy = g_policy.getLocal()->name;
+ if (pool->policy != nullptr) {
+ policy = pool->policy->name;
+ }
+ string servers;
+
+ for (const auto& server: pool->servers) {
+ if (!servers.empty()) {
+ servers += ", ";
+ }
+ if (!server.second->name.empty()) {
+ servers += server.second->name;
+ servers += " ";
+ }
+ servers += server.second->remote.toStringWithPort();
+ }
+
+ ret << (fmt % name % cache % policy % servers) << endl;
+ }
+ g_outputBuffer=ret.str();
+ }catch(std::exception& e) { g_outputBuffer=e.what(); throw; }
+ });
+
+ g_lua.writeFunction("getPool", [client](const string& poolName) {
+ if (client) {
+ return std::make_shared<ServerPool>();
}
+ auto localPools = g_pools.getCopy();
+ std::shared_ptr<ServerPool> pool = createPoolIfNotExists(localPools, poolName);
+ g_pools.setState(localPools);
+ return pool;
});
+ g_lua.writeFunction("setVerboseHealthChecks", [](bool verbose) { g_verboseHealthChecks=verbose; });
+ g_lua.writeFunction("setStaleCacheEntriesTTL", [](uint32_t ttl) { g_staleCacheEntriesTTL = ttl; });
- g_lua.writeFunction("topClients", [](boost::optional<unsigned int> top_) {
+ g_lua.writeFunction("showBinds", []() {
setLuaNoSideEffect();
- auto top = top_.get_value_or(10);
- map<ComboAddress, int,ComboAddress::addressOnlyLessThan > counts;
- unsigned int total=0;
- {
- ReadLock rl(&g_rings.queryLock);
- for(const auto& c : g_rings.queryRing) {
- counts[c.requestor]++;
- total++;
+ try {
+ ostringstream ret;
+ boost::format fmt("%1$-3d %2$-20.20s %|25t|%3$-8.8s %|35t|%4%" );
+ // 1 2 3 4
+ ret << (fmt % "#" % "Address" % "Protocol" % "Queries" ) << endl;
+
+ size_t counter = 0;
+ for (const auto& front : g_frontends) {
+ ret << (fmt % counter % front->local.toStringWithPort() % (front->udpFD != -1 ? "UDP" : "TCP") % front->queries) << endl;
+ counter++;
}
- }
- vector<pair<int, ComboAddress>> rcounts;
- rcounts.reserve(counts.size());
- for(const auto& c : counts)
- rcounts.push_back(make_pair(c.second, c.first));
-
- sort(rcounts.begin(), rcounts.end(), [](const decltype(rcounts)::value_type& a,
- const decltype(rcounts)::value_type& b) {
- return b.first < a.first;
- });
- unsigned int count=1, rest=0;
- boost::format fmt("%4d %-40s %4d %4.1f%%\n");
- for(const auto& rc : rcounts) {
- if(count==top+1)
- rest+=rc.first;
- else
- g_outputBuffer += (fmt % (count++) % rc.second.toString() % rc.first % (100.0*rc.first/total)).str();
- }
- g_outputBuffer += (fmt % (count) % "Rest" % rest % (total > 0 ? 100.0*rest/total : 100.0)).str();
+ g_outputBuffer=ret.str();
+ }catch(std::exception& e) { g_outputBuffer=e.what(); throw; }
});
- g_lua.writeFunction("getTopQueries", [](unsigned int top, boost::optional<int> labels) {
+ g_lua.writeFunction("getBind", [](size_t num) {
setLuaNoSideEffect();
- map<DNSName, int> counts;
- unsigned int total=0;
- if(!labels) {
- ReadLock rl(&g_rings.queryLock);
- for(const auto& a : g_rings.queryRing) {
- counts[a.name]++;
- total++;
- }
- }
- else {
- unsigned int lab = *labels;
- ReadLock rl(&g_rings.queryLock);
- for(auto a : g_rings.queryRing) {
- a.name.trimToLabels(lab);
- counts[a.name]++;
- total++;
- }
- }
- // cout<<"Looked at "<<total<<" queries, "<<counts.size()<<" different ones"<<endl;
- vector<pair<int, DNSName>> rcounts;
- rcounts.reserve(counts.size());
- for(const auto& c : counts)
- rcounts.push_back(make_pair(c.second, c.first.makeLowerCase()));
-
- sort(rcounts.begin(), rcounts.end(), [](const decltype(rcounts)::value_type& a,
- const decltype(rcounts)::value_type& b) {
- return b.first < a.first;
- });
-
- std::unordered_map<int, vector<boost::variant<string,double>>> ret;
- unsigned int count=1, rest=0;
- for(const auto& rc : rcounts) {
- if(count==top+1)
- rest+=rc.first;
- else
- ret.insert({count++, {rc.second.toString(), rc.first, 100.0*rc.first/total}});
+ ClientState* ret = nullptr;
+ if(num < g_frontends.size()) {
+ ret=g_frontends[num];
}
- ret.insert({count, {"Rest", rest, total > 0 ? 100.0*rest/total : 100.0}});
return ret;
+ });
- });
-
- g_lua.executeCode(R"(function topQueries(top, labels) top = top or 10; for k,v in ipairs(getTopQueries(top,labels)) do show(string.format("%4d %-40s %4d %4.1f%%",k,v[1],v[2], v[3])) end end)");
-
- g_lua.writeFunction("clearQueryCounters", []() {
- unsigned int size{0};
- {
- WriteLock wl(&g_qcount.queryLock);
- size = g_qcount.records.size();
- g_qcount.records.clear();
+ g_lua.writeFunction("help", [](boost::optional<std::string> command) {
+ setLuaNoSideEffect();
+ g_outputBuffer = "";
+ for (const auto& keyword : g_consoleKeywords) {
+ if (!command) {
+ g_outputBuffer += keyword.toString() + "\n";
+ }
+ else if (keyword.name == command) {
+ g_outputBuffer = keyword.toString() + "\n";
+ return;
+ }
+ }
+ if (command) {
+ g_outputBuffer = "Nothing found for " + *command + "\n";
}
-
- boost::format fmt("%d records cleared from query counter buffer\n");
- g_outputBuffer = (fmt % size).str();
});
- g_lua.writeFunction("getQueryCounters", [](boost::optional<unsigned int> optMax) {
+ g_lua.writeFunction("showVersion", []() {
setLuaNoSideEffect();
- ReadLock rl(&g_qcount.queryLock);
- g_outputBuffer = "query counting is currently: ";
- g_outputBuffer+= g_qcount.enabled ? "enabled" : "disabled";
- g_outputBuffer+= (boost::format(" (%d records in buffer)\n") % g_qcount.records.size()).str();
-
- boost::format fmt("%-3d %s: %d request(s)\n");
- QueryCountRecords::iterator it;
- unsigned int max = optMax ? *optMax : 10;
- unsigned int index{1};
- for(it = g_qcount.records.begin(); it != g_qcount.records.end() && index <= max; ++it, ++index) {
- g_outputBuffer += (fmt % index % it->first % it->second).str();
- }
+ g_outputBuffer = "dnsdist " + std::string(VERSION) + "\n";
});
- g_lua.writeFunction("setQueryCount", [](bool enabled) { g_qcount.enabled=enabled; });
- g_lua.writeFunction("setQueryCountFilter", [](QueryCountFilter func) {
- g_qcount.filter = func;
+#ifdef HAVE_EBPF
+ g_lua.writeFunction("setDefaultBPFFilter", [](std::shared_ptr<BPFFilter> bpf) {
+ if (g_configurationDone) {
+ g_outputBuffer="setDefaultBPFFilter() cannot be used at runtime!\n";
+ return;
+ }
+ g_defaultBPFFilter = bpf;
});
- g_lua.writeFunction("getResponseRing", []() {
- setLuaNoSideEffect();
- decltype(g_rings.respRing) ring;
- {
- std::lock_guard<std::mutex> lock(g_rings.respMutex);
- ring = g_rings.respRing;
+ g_lua.writeFunction("registerDynBPFFilter", [](std::shared_ptr<DynBPFFilter> dbpf) {
+ if (dbpf) {
+ g_dynBPFFilters.push_back(dbpf);
}
- vector<std::unordered_map<string, boost::variant<string, unsigned int> > > ret;
- ret.reserve(ring.size());
- decltype(ret)::value_type item;
- for(const auto& r : ring) {
- item["name"]=r.name.toString();
- item["qtype"]=r.qtype;
- item["rcode"]=r.dh.rcode;
- item["usec"]=r.usec;
- ret.push_back(item);
- }
- return ret;
});
- g_lua.writeFunction("getTopResponses", [](unsigned int top, unsigned int kind, boost::optional<int> labels) {
- return getGenResponses(top, labels, [kind](const Rings::Response& r) { return r.dh.rcode == kind; });
+ g_lua.writeFunction("unregisterDynBPFFilter", [](std::shared_ptr<DynBPFFilter> dbpf) {
+ if (dbpf) {
+ for (auto it = g_dynBPFFilters.begin(); it != g_dynBPFFilters.end(); it++) {
+ if (*it == dbpf) {
+ g_dynBPFFilters.erase(it);
+ break;
+ }
+ }
+ }
});
- g_lua.executeCode(R"(function topResponses(top, kind, labels) top = top or 10; kind = kind or 0; for k,v in ipairs(getTopResponses(top, kind, labels)) do show(string.format("%4d %-40s %4d %4.1f%%",k,v[1],v[2],v[3])) end end)");
-
-
- g_lua.writeFunction("getSlowResponses", [](unsigned int top, unsigned int msec, boost::optional<int> labels) {
- return getGenResponses(top, labels, [msec](const Rings::Response& r) { return r.usec > msec*1000; });
+ g_lua.writeFunction("addBPFFilterDynBlocks", [](const map<ComboAddress,int>& m, std::shared_ptr<DynBPFFilter> dynbpf, boost::optional<int> seconds) {
+ setLuaSideEffect();
+ struct timespec until, now;
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ until=now;
+ int actualSeconds = seconds ? *seconds : 10;
+ until.tv_sec += actualSeconds;
+ for(const auto& capair : m) {
+ dynbpf->block(capair.first, until);
+ }
});
+#endif /* HAVE_EBPF */
- g_lua.executeCode(R"(function topSlow(top, msec, labels) top = top or 10; msec = msec or 500; for k,v in ipairs(getSlowResponses(top, msec, labels)) do show(string.format("%4d %-40s %4d %4.1f%%",k,v[1],v[2],v[3])) end end)");
-
-
- g_lua.writeFunction("showResponseLatency", []() {
+ g_lua.writeFunction<std::unordered_map<string,uint64_t>()>("getStatisticsCounters", []() {
setLuaNoSideEffect();
- map<double, unsigned int> histo;
- double bin=100;
- for(int i=0; i < 15; ++i) {
- histo[bin];
- bin*=2;
+ std::unordered_map<string,uint64_t> res;
+ for(const auto& entry : g_stats.entries) {
+ if(const auto& val = boost::get<DNSDistStats::stat_t*>(&entry.second))
+ res[entry.first] = (*val)->load();
}
+ return res;
+ });
- double totlat=0;
- unsigned int size=0;
- {
- std::lock_guard<std::mutex> lock(g_rings.respMutex);
- for(const auto& r : g_rings.respRing) {
- /* skip actively discovered timeouts */
- if (r.usec == std::numeric_limits<unsigned int>::max())
- continue;
-
- ++size;
- auto iter = histo.lower_bound(r.usec);
- if(iter != histo.end())
- iter->second++;
- else
- histo.rbegin()++;
- totlat+=r.usec;
- }
+ g_lua.writeFunction("includeDirectory", [](const std::string& dirname) {
+ if (g_configurationDone) {
+ errlog("includeDirectory() cannot be used at runtime!");
+ g_outputBuffer="includeDirectory() cannot be used at runtime!\n";
+ return;
}
- if (size == 0) {
- g_outputBuffer = "No traffic yet.\n";
+ if (g_included) {
+ errlog("includeDirectory() cannot be used recursively!");
+ g_outputBuffer="includeDirectory() cannot be used recursively!\n";
return;
}
- g_outputBuffer = (boost::format("Average response latency: %.02f msec\n") % (0.001*totlat/size)).str();
- double highest=0;
-
- for(auto iter = histo.cbegin(); iter != histo.cend(); ++iter) {
- highest=std::max(highest, iter->second*1.0);
- }
- boost::format fmt("%7.2f\t%s\n");
- g_outputBuffer += (fmt % "msec" % "").str();
-
- for(auto iter = histo.cbegin(); iter != histo.cend(); ++iter) {
- int stars = (70.0 * iter->second/highest);
- char c='*';
- if(!stars && iter->second) {
- stars=1; // you get 1 . to show something is there..
- if(70.0*iter->second/highest > 0.5)
- c=':';
- else
- c='.';
- }
- g_outputBuffer += (fmt % (iter->first/1000.0) % string(stars, c)).str();
+ g_included = true;
+ struct stat st;
+ if (stat(dirname.c_str(), &st)) {
+ errlog("The included directory %s does not exist!", dirname.c_str());
+ g_outputBuffer="The included directory " + dirname + " does not exist!";
+ return;
}
- });
-
- g_lua.writeFunction("newQPSLimiter", [](int rate, int burst) { return QPSLimiter(rate, burst); });
- g_lua.registerFunction("check", &QPSLimiter::check);
-
- g_lua.writeFunction("makeKey", []() {
- setLuaNoSideEffect();
- g_outputBuffer="setKey("+newKey()+")\n";
- });
-
- g_lua.writeFunction("setKey", [](const std::string& key) {
- if(!g_configurationDone && ! g_key.empty()) { // this makes sure the commandline -k key prevails over dnsdist.conf
- return; // but later setKeys() trump the -k value again
+ if (!S_ISDIR(st.st_mode)) {
+ errlog("The included directory %s is not a directory!", dirname.c_str());
+ g_outputBuffer="The included directory " + dirname + " is not a directory!";
+ return;
}
- setLuaSideEffect();
- string newkey;
- if(B64Decode(key, newkey) < 0) {
- g_outputBuffer=string("Unable to decode ")+key+" as Base64";
- errlog("%s", g_outputBuffer);
+ DIR *dirp;
+ struct dirent *ent;
+ std::list<std::string> files;
+ if (!(dirp = opendir(dirname.c_str()))) {
+ errlog("Error opening the included directory %s!", dirname.c_str());
+ g_outputBuffer="Error opening the included directory " + dirname + "!";
+ return;
}
- else
- g_key=newkey;
- });
-
- g_lua.writeFunction("testCrypto", [](boost::optional<string> optTestMsg)
- {
- setLuaNoSideEffect();
-#ifdef HAVE_LIBSODIUM
- try {
- string testmsg;
-
- if (optTestMsg) {
- testmsg = *optTestMsg;
- }
- else {
- testmsg = "testStringForCryptoTests";
- }
-
- SodiumNonce sn, sn2;
- sn.init();
- sn2=sn;
- string encrypted = sodEncryptSym(testmsg, g_key, sn);
- string decrypted = sodDecryptSym(encrypted, g_key, sn2);
-
- sn.increment();
- sn2.increment();
-
- encrypted = sodEncryptSym(testmsg, g_key, sn);
- decrypted = sodDecryptSym(encrypted, g_key, sn2);
-
- if(testmsg == decrypted)
- g_outputBuffer="Everything is ok!\n";
- else
- g_outputBuffer="Crypto failed..\n";
-
- }
- catch(...) {
- g_outputBuffer="Crypto failed..\n";
- }
-#else
- g_outputBuffer="Crypto not available.\n";
-#endif
- });
-
- g_lua.writeFunction("setTCPRecvTimeout", [](int timeout) { g_tcpRecvTimeout=timeout; });
+ while((ent = readdir(dirp)) != NULL) {
+ if (ent->d_name[0] == '.') {
+ continue;
+ }
- g_lua.writeFunction("setTCPSendTimeout", [](int timeout) { g_tcpSendTimeout=timeout; });
+ if (boost::ends_with(ent->d_name, ".conf")) {
+ std::ostringstream namebuf;
+ namebuf << dirname.c_str() << "/" << ent->d_name;
- g_lua.writeFunction("setUDPTimeout", [](int timeout) { g_udpTimeout=timeout; });
+ if (stat(namebuf.str().c_str(), &st) || !S_ISREG(st.st_mode)) {
+ continue;
+ }
- g_lua.writeFunction("setMaxUDPOutstanding", [](uint16_t max) {
- if (!g_configurationDone) {
- g_maxOutstanding = max;
- } else {
- g_outputBuffer="Max UDP outstanding cannot be altered at runtime!\n";
+ files.push_back(namebuf.str());
+ }
}
- });
- g_lua.registerFunction<void(DNSQuestion::*)(std::string, std::string)>("setTag", [](DNSQuestion& dq, const std::string& strLabel, const std::string& strValue) {
+ closedir(dirp);
+ files.sort();
+
+ for (auto file = files.begin(); file != files.end(); ++file) {
+ std::ifstream ifs(*file);
+ if (!ifs) {
+ warnlog("Unable to read configuration from '%s'", *file);
+ } else {
+ vinfolog("Read configuration from '%s'", *file);
+ }
- if(dq.qTag == nullptr) {
- dq.qTag = std::make_shared<QTag>();
+ g_lua.executeCode(ifs);
}
- dq.qTag->add(strLabel, strValue);
+ g_included = false;
});
- g_lua.registerFunction<void(DNSQuestion::*)(vector<pair<string, string>>)>("setTagArray", [](DNSQuestion& dq, const vector<pair<string, string>>&tags) {
-
- if(dq.qTag == nullptr) {
- dq.qTag = std::make_shared<QTag>();
- }
-
- for (const auto& tag : tags) {
- dq.qTag->add(tag.first, tag.second);
+ g_lua.writeFunction("setAPIWritable", [](bool writable, boost::optional<std::string> apiConfigDir) {
+ setLuaSideEffect();
+ g_apiReadWrite = writable;
+ if (apiConfigDir) {
+ if (!(*apiConfigDir).empty()) {
+ g_apiConfigDirectory = *apiConfigDir;
+ }
+ else {
+ errlog("The API configuration directory value cannot be empty!");
+ g_outputBuffer="The API configuration directory value cannot be empty!";
+ }
}
-
});
- g_lua.registerFunction<string(DNSQuestion::*)(std::string)>("getTag", [](const DNSQuestion& dq, const std::string& strLabel) {
+ g_lua.writeFunction("setServFailWhenNoServer", [](bool servfail) {
+ setLuaSideEffect();
+ g_servFailOnNoPolicy = servfail;
+ });
- std::string strValue;
- if(dq.qTag != nullptr) {
- strValue = dq.qTag->getMatch(strLabel);
+ g_lua.writeFunction("setRingBuffersSize", [](size_t capacity) {
+ setLuaSideEffect();
+ if (g_configurationDone) {
+ errlog("setRingBuffersSize() cannot be used at runtime!");
+ g_outputBuffer="setRingBuffersSize() cannot be used at runtime!\n";
+ return;
}
- return strValue;
-
+ g_rings.setCapacity(capacity);
});
+ g_lua.writeFunction("setWHashedPertubation", [](uint32_t pertub) {
+ setLuaSideEffect();
+ g_hashperturb = pertub;
+ });
- g_lua.registerFunction<std::unordered_map<string, string>(DNSQuestion::*)(void)>("getTagArray", [](const DNSQuestion& dq) {
-
- if(dq.qTag != nullptr) {
- return dq.qTag->tagData;
- } else {
- std::unordered_map<string, string> XX;
- return XX;
+ g_lua.writeFunction("setTCPUseSinglePipe", [](bool flag) {
+ if (g_configurationDone) {
+ g_outputBuffer="setTCPUseSinglePipe() cannot be used at runtime!\n";
+ return;
}
+ setLuaSideEffect();
+ g_useTCPSinglePipe = flag;
});
-
- /* DNSQuestion bindings */
- /* PowerDNS DNSQuestion compat */
- g_lua.registerMember<const ComboAddress (DNSQuestion::*)>("localaddr", [](const DNSQuestion& dq) -> const ComboAddress { return *dq.local; }, [](DNSQuestion& dq, const ComboAddress newLocal) { (void) newLocal; });
- g_lua.registerMember<const DNSName (DNSQuestion::*)>("qname", [](const DNSQuestion& dq) -> const DNSName { return *dq.qname; }, [](DNSQuestion& dq, const DNSName newName) { (void) newName; });
- g_lua.registerMember<uint16_t (DNSQuestion::*)>("qtype", [](const DNSQuestion& dq) -> uint16_t { return dq.qtype; }, [](DNSQuestion& dq, uint16_t newType) { (void) newType; });
- g_lua.registerMember<uint16_t (DNSQuestion::*)>("qclass", [](const DNSQuestion& dq) -> uint16_t { return dq.qclass; }, [](DNSQuestion& dq, uint16_t newClass) { (void) newClass; });
- g_lua.registerMember<int (DNSQuestion::*)>("rcode", [](const DNSQuestion& dq) -> int { return dq.dh->rcode; }, [](DNSQuestion& dq, int newRCode) { dq.dh->rcode = newRCode; });
- g_lua.registerMember<const ComboAddress (DNSQuestion::*)>("remoteaddr", [](const DNSQuestion& dq) -> const ComboAddress { return *dq.remote; }, [](DNSQuestion& dq, const ComboAddress newRemote) { (void) newRemote; });
- /* DNSDist DNSQuestion */
- g_lua.registerMember("dh", &DNSQuestion::dh);
- g_lua.registerMember<uint16_t (DNSQuestion::*)>("len", [](const DNSQuestion& dq) -> uint16_t { return dq.len; }, [](DNSQuestion& dq, uint16_t newlen) { dq.len = newlen; });
- g_lua.registerMember<uint8_t (DNSQuestion::*)>("opcode", [](const DNSQuestion& dq) -> uint8_t { return dq.dh->opcode; }, [](DNSQuestion& dq, uint8_t newOpcode) { (void) newOpcode; });
- g_lua.registerMember<size_t (DNSQuestion::*)>("size", [](const DNSQuestion& dq) -> size_t { return dq.size; }, [](DNSQuestion& dq, size_t newSize) { (void) newSize; });
- g_lua.registerMember<bool (DNSQuestion::*)>("tcp", [](const DNSQuestion& dq) -> bool { return dq.tcp; }, [](DNSQuestion& dq, bool newTcp) { (void) newTcp; });
- g_lua.registerMember<bool (DNSQuestion::*)>("skipCache", [](const DNSQuestion& dq) -> bool { return dq.skipCache; }, [](DNSQuestion& dq, bool newSkipCache) { dq.skipCache = newSkipCache; });
- g_lua.registerMember<bool (DNSQuestion::*)>("useECS", [](const DNSQuestion& dq) -> bool { return dq.useECS; }, [](DNSQuestion& dq, bool useECS) { dq.useECS = useECS; });
- g_lua.registerMember<bool (DNSQuestion::*)>("ecsOverride", [](const DNSQuestion& dq) -> bool { return dq.ecsOverride; }, [](DNSQuestion& dq, bool ecsOverride) { dq.ecsOverride = ecsOverride; });
- g_lua.registerMember<uint16_t (DNSQuestion::*)>("ecsPrefixLength", [](const DNSQuestion& dq) -> uint16_t { return dq.ecsPrefixLength; }, [](DNSQuestion& dq, uint16_t newPrefixLength) { dq.ecsPrefixLength = newPrefixLength; });
- g_lua.registerFunction<bool(DNSQuestion::*)()>("getDO", [](const DNSQuestion& dq) {
- return getEDNSZ((const char*)dq.dh, dq.len) & EDNS_HEADER_FLAG_DO;
- });
- g_lua.registerFunction<void(DNSQuestion::*)(std::string)>("sendTrap", [](const DNSQuestion& dq, boost::optional<std::string> reason) {
+ g_lua.writeFunction("snmpAgent", [](bool enableTraps, boost::optional<std::string> masterSocket) {
#ifdef HAVE_NET_SNMP
- if (g_snmpAgent && g_snmpTrapsEnabled) {
- g_snmpAgent->sendDNSTrap(dq, reason ? *reason : "");
+ if (g_configurationDone) {
+ errlog("snmpAgent() cannot be used at runtime!");
+ g_outputBuffer="snmpAgent() cannot be used at runtime!\n";
+ return;
+ }
+
+ if (g_snmpEnabled) {
+ errlog("snmpAgent() cannot be used twice!");
+ g_outputBuffer="snmpAgent() cannot be used twice!\n";
+ return;
}
+
+ g_snmpEnabled = true;
+ g_snmpTrapsEnabled = enableTraps;
+ g_snmpAgent = new DNSDistSNMPAgent("dnsdist", masterSocket ? *masterSocket : std::string());
+#else
+ errlog("NET SNMP support is required to use snmpAgent()");
+ g_outputBuffer="NET SNMP support is required to use snmpAgent()\n";
#endif /* HAVE_NET_SNMP */
});
- /* LuaWrapper doesn't support inheritance */
- g_lua.registerMember<const ComboAddress (DNSResponse::*)>("localaddr", [](const DNSResponse& dq) -> const ComboAddress { return *dq.local; }, [](DNSResponse& dq, const ComboAddress newLocal) { (void) newLocal; });
- g_lua.registerMember<const DNSName (DNSResponse::*)>("qname", [](const DNSResponse& dq) -> const DNSName { return *dq.qname; }, [](DNSResponse& dq, const DNSName newName) { (void) newName; });
- g_lua.registerMember<uint16_t (DNSResponse::*)>("qtype", [](const DNSResponse& dq) -> uint16_t { return dq.qtype; }, [](DNSResponse& dq, uint16_t newType) { (void) newType; });
- g_lua.registerMember<uint16_t (DNSResponse::*)>("qclass", [](const DNSResponse& dq) -> uint16_t { return dq.qclass; }, [](DNSResponse& dq, uint16_t newClass) { (void) newClass; });
- g_lua.registerMember<int (DNSResponse::*)>("rcode", [](const DNSResponse& dq) -> int { return dq.dh->rcode; }, [](DNSResponse& dq, int newRCode) { dq.dh->rcode = newRCode; });
- g_lua.registerMember<const ComboAddress (DNSResponse::*)>("remoteaddr", [](const DNSResponse& dq) -> const ComboAddress { return *dq.remote; }, [](DNSResponse& dq, const ComboAddress newRemote) { (void) newRemote; });
- g_lua.registerMember<dnsheader* (DNSResponse::*)>("dh", [](const DNSResponse& dr) -> dnsheader* { return dr.dh; }, [](DNSResponse& dr, dnsheader * newdh) { dr.dh = newdh; });
- g_lua.registerMember<uint16_t (DNSResponse::*)>("len", [](const DNSResponse& dq) -> uint16_t { return dq.len; }, [](DNSResponse& dq, uint16_t newlen) { dq.len = newlen; });
- g_lua.registerMember<uint8_t (DNSResponse::*)>("opcode", [](const DNSResponse& dq) -> uint8_t { return dq.dh->opcode; }, [](DNSResponse& dq, uint8_t newOpcode) { (void) newOpcode; });
- g_lua.registerMember<size_t (DNSResponse::*)>("size", [](const DNSResponse& dq) -> size_t { return dq.size; }, [](DNSResponse& dq, size_t newSize) { (void) newSize; });
- g_lua.registerMember<bool (DNSResponse::*)>("tcp", [](const DNSResponse& dq) -> bool { return dq.tcp; }, [](DNSResponse& dq, bool newTcp) { (void) newTcp; });
- g_lua.registerMember<bool (DNSResponse::*)>("skipCache", [](const DNSResponse& dq) -> bool { return dq.skipCache; }, [](DNSResponse& dq, bool newSkipCache) { dq.skipCache = newSkipCache; });
- g_lua.registerFunction<void(DNSResponse::*)(std::function<uint32_t(uint8_t section, uint16_t qclass, uint16_t qtype, uint32_t ttl)> editFunc)>("editTTLs", [](const DNSResponse& dr, std::function<uint32_t(uint8_t section, uint16_t qclass, uint16_t qtype, uint32_t ttl)> editFunc) {
- editDNSPacketTTL((char*) dr.dh, dr.len, editFunc);
- });
- g_lua.registerFunction<void(DNSResponse::*)(std::string)>("sendTrap", [](const DNSResponse& dr, boost::optional<std::string> reason) {
+ g_lua.writeFunction("sendCustomTrap", [](const std::string& str) {
#ifdef HAVE_NET_SNMP
if (g_snmpAgent && g_snmpTrapsEnabled) {
- g_snmpAgent->sendDNSTrap(dr, reason ? *reason : "");
+ g_snmpAgent->sendCustomTrap(str);
}
#endif /* HAVE_NET_SNMP */
});
- g_lua.writeFunction("setMaxTCPClientThreads", [](uint64_t max) {
- if (!g_configurationDone) {
- g_maxTCPClientThreads = max;
- } else {
- g_outputBuffer="Maximum TCP client threads count cannot be altered at runtime!\n";
- }
- });
-
- g_lua.writeFunction("setMaxTCPQueuedConnections", [](uint64_t max) {
- if (!g_configurationDone) {
- g_maxTCPQueuedConnections = max;
- } else {
- g_outputBuffer="The maximum number of queued TCP connections cannot be altered at runtime!\n";
- }
+ g_lua.writeFunction("setPoolServerPolicy", [](ServerPolicy policy, string pool) {
+ setLuaSideEffect();
+ auto localPools = g_pools.getCopy();
+ setPoolPolicy(localPools, pool, std::make_shared<ServerPolicy>(policy));
+ g_pools.setState(localPools);
});
- g_lua.writeFunction("setMaxTCPQueriesPerConnection", [](size_t max) {
- if (!g_configurationDone) {
- g_maxTCPQueriesPerConn = max;
- } else {
- g_outputBuffer="The maximum number of queries per TCP connection cannot be altered at runtime!\n";
- }
+ g_lua.writeFunction("setPoolServerPolicyLua", [](string name, policyfunc_t policy, string pool) {
+ setLuaSideEffect();
+ auto localPools = g_pools.getCopy();
+ setPoolPolicy(localPools, pool, std::make_shared<ServerPolicy>(ServerPolicy{name, policy}));
+ g_pools.setState(localPools);
});
- g_lua.writeFunction("setMaxTCPConnectionsPerClient", [](size_t max) {
- if (!g_configurationDone) {
- g_maxTCPConnectionsPerClient = max;
+ g_lua.writeFunction("showPoolServerPolicy", [](string pool) {
+ setLuaSideEffect();
+ auto localPools = g_pools.getCopy();
+ auto poolObj = getPool(localPools, pool);
+ if (poolObj->policy == nullptr) {
+ g_outputBuffer=g_policy.getLocal()->name+"\n";
} else {
- g_outputBuffer="The maximum number of TCP connection per client cannot be altered at runtime!\n";
+ g_outputBuffer=poolObj->policy->name+"\n";
}
});
- g_lua.writeFunction("setMaxTCPConnectionDuration", [](size_t max) {
- if (!g_configurationDone) {
- g_maxTCPConnectionDuration = max;
- } else {
- g_outputBuffer="The maximum duration of a TCP connection cannot be altered at runtime!\n";
- }
+ g_lua.writeFunction("setTCPDownstreamCleanupInterval", [](uint16_t interval) {
+ setLuaSideEffect();
+ g_downstreamTCPCleanupInterval = interval;
});
- g_lua.writeFunction("showTCPStats", [] {
- setLuaNoSideEffect();
- boost::format fmt("%-10d %-10d %-10d %-10d\n");
- g_outputBuffer += (fmt % "Clients" % "MaxClients" % "Queued" % "MaxQueued").str();
- g_outputBuffer += (fmt % g_tcpclientthreads->getThreadsCount() % g_maxTCPClientThreads % g_tcpclientthreads->getQueuedCount() % g_maxTCPQueuedConnections).str();
- g_outputBuffer += "Query distribution mode is: " + std::string(g_useTCPSinglePipe ? "single queue" : "per-thread queues") + "\n";
+ g_lua.writeFunction("setConsoleConnectionsLogging", [](bool enabled) {
+ g_logConsoleConnections = enabled;
});
- g_lua.writeFunction("setCacheCleaningDelay", [](uint32_t delay) { g_cacheCleaningDelay = delay; });
- g_lua.writeFunction("setCacheCleaningPercentage", [](uint16_t percentage) { if (percentage < 100) g_cacheCleaningPercentage = percentage; else g_cacheCleaningPercentage = 100; });
-
- g_lua.writeFunction("setECSSourcePrefixV4", [](uint16_t prefix) { g_ECSSourcePrefixV4=prefix; });
-
- g_lua.writeFunction("setECSSourcePrefixV6", [](uint16_t prefix) { g_ECSSourcePrefixV6=prefix; });
-
- g_lua.writeFunction("setECSOverride", [](bool override) { g_ECSOverride=override; });
-
- g_lua.writeFunction("dumpStats", [] {
- setLuaNoSideEffect();
- vector<string> leftcolumn, rightcolumn;
-
- boost::format fmt("%-23s\t%+11s");
- g_outputBuffer.clear();
- auto entries = g_stats.entries;
- sort(entries.begin(), entries.end(),
- [](const decltype(entries)::value_type& a, const decltype(entries)::value_type& b) {
- return a.first < b.first;
- });
- boost::format flt(" %9.1f");
- for(const auto& e : entries) {
- string second;
- if(const auto& val = boost::get<DNSDistStats::stat_t*>(&e.second))
- second=std::to_string((*val)->load());
- else if (const auto& dval = boost::get<double*>(&e.second))
- second=(flt % (**dval)).str();
- else
- second=std::to_string((*boost::get<DNSDistStats::statfunction_t>(&e.second))(e.first));
-
- if(leftcolumn.size() < g_stats.entries.size()/2)
- leftcolumn.push_back((fmt % e.first % second).str());
- else
- rightcolumn.push_back((fmt % e.first % second).str());
+ g_lua.writeFunction("setUDPMultipleMessagesVectorSize", [](size_t vSize) {
+ if (g_configurationDone) {
+ errlog("setUDPMultipleMessagesVectorSize() cannot be used at runtime!");
+ g_outputBuffer="setUDPMultipleMessagesVectorSize() cannot be used at runtime!\n";
+ return;
}
+#if defined(HAVE_RECVMMSG) && defined(HAVE_SENDMMSG) && defined(MSG_WAITFORONE)
+ setLuaSideEffect();
+ g_udpVectorSize = vSize;
+#else
+ errlog("recvmmsg() support is not available!");
+ g_outputBuffer="recvmmsg support is not available!\n";
+#endif
+ });
+}
- auto leftiter=leftcolumn.begin(), rightiter=rightcolumn.begin();
- boost::format clmn("%|0t|%1% %|39t|%2%\n");
+vector<std::function<void(void)>> setupLua(bool client, const std::string& config)
+{
+ g_launchWork= new vector<std::function<void(void)>>();
- for(;leftiter != leftcolumn.end() || rightiter != rightcolumn.end();) {
- string lentry, rentry;
- if(leftiter!= leftcolumn.end()) {
- lentry = *leftiter;
- leftiter++;
- }
- if(rightiter!= rightcolumn.end()) {
- rentry = *rightiter;
- rightiter++;
- }
- g_outputBuffer += (clmn % lentry % rentry).str();
- }
- });
+ setupLuaActions();
+ setupLuaConfig(client);
+ setupLuaBindings(client);
+ setupLuaBindingsDNSQuestion();
+ setupLuaInspection();
+ setupLuaRules();
+ setupLuaVars();
- moreLua(client);
-
std::ifstream ifs(config);
- if(!ifs)
+ if(!ifs)
warnlog("Unable to read configuration from '%s'", config);
else
vinfolog("Read configuration from '%s'", config);
g_lua.executeCode(ifs);
- auto ret=*g_launchWork;
+ auto ret = *g_launchWork;
delete g_launchWork;
- g_launchWork=0;
+ g_launchWork = nullptr;
return ret;
}
*/
#pragma once
-typedef std::unordered_map<std::string, boost::variant<bool, int, std::string, std::vector<std::pair<int,int> > > > localbind_t;
-void parseLocalBindVars(boost::optional<localbind_t> vars, bool& doTCP, bool& reusePort, int& tcpFastOpenQueueSize, std::string& interface, std::set<int>& cpus);
+class LuaAction : public DNSAction
+{
+public:
+ typedef std::function<std::tuple<int, string>(DNSQuestion* dq)> func_t;
+ LuaAction(LuaAction::func_t func) : d_func(func)
+ {}
+
+ Action operator()(DNSQuestion* dq, string* ruleresult) const override
+ {
+ std::lock_guard<std::mutex> lock(g_luamutex);
+ auto ret = d_func(dq);
+ if(ruleresult)
+ *ruleresult=std::get<1>(ret);
+ return (Action)std::get<0>(ret);
+ }
+
+ string toString() const override
+ {
+ return "Lua script";
+ }
+
+private:
+ func_t d_func;
+};
+
+class LuaResponseAction : public DNSResponseAction
+{
+public:
+ typedef std::function<std::tuple<int, string>(DNSResponse* dr)> func_t;
+ LuaResponseAction(LuaResponseAction::func_t func) : d_func(func)
+ {}
+
+ Action operator()(DNSResponse* dr, string* ruleresult) const override
+ {
+ std::lock_guard<std::mutex> lock(g_luamutex);
+ auto ret = d_func(dr);
+ if(ruleresult)
+ *ruleresult=std::get<1>(ret);
+ return (Action)std::get<0>(ret);
+ }
+
+ string toString() const override
+ {
+ return "Lua response script";
+ }
+
+private:
+ func_t d_func;
+};
+
+class SpoofAction : public DNSAction
+{
+public:
+ SpoofAction(const vector<ComboAddress>& addrs): d_addrs(addrs)
+ {
+ }
+ SpoofAction(const string& cname): d_cname(cname)
+ {
+ }
+ DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override;
+ string toString() const override
+ {
+ string ret = "spoof in ";
+ if(!d_cname.empty()) {
+ ret+=d_cname.toString()+ " ";
+ } else {
+ for(const auto& a : d_addrs)
+ ret += a.toString()+" ";
+ }
+ return ret;
+ }
+private:
+ std::vector<ComboAddress> d_addrs;
+ DNSName d_cname;
+};
typedef boost::variant<string, vector<pair<int, string>>, std::shared_ptr<DNSRule>, DNSName, vector<pair<int, DNSName> > > luadnsrule_t;
std::shared_ptr<DNSRule> makeRule(const luadnsrule_t& var);
+
+typedef NetmaskTree<DynBlock> nmts_t;
+
+void setupLuaActions();
+void setupLuaBindings(bool client);
+void setupLuaBindingsDNSQuestion();
+void setupLuaRules();
+void setupLuaInspection();
+void setupLuaVars();
+++ /dev/null
-/*
- * 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-cache.hh"
-#include "dnsrulactions.hh"
-#include <thread>
-#include "dolog.hh"
-#include "sodcrypto.hh"
-#include "base64.hh"
-#include "lock.hh"
-#include "gettime.hh"
-#include <map>
-#include <fstream>
-#include <boost/logic/tribool.hpp>
-#include "statnode.hh"
-#include <sys/types.h>
-#include <dirent.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#include "dnsdist-lua.hh"
-
-boost::tribool g_noLuaSideEffect;
-static bool g_included{false};
-
-/* this is a best effort way to prevent logging calls with no side-effects in the output of delta()
- Functions can declare setLuaNoSideEffect() and if nothing else does declare a side effect, or nothing
- has done so before on this invocation, this call won't be part of delta() output */
-void setLuaNoSideEffect()
-{
- if(g_noLuaSideEffect==false) // there has been a side effect already
- return;
- g_noLuaSideEffect=true;
-}
-
-void setLuaSideEffect()
-{
- g_noLuaSideEffect=false;
-}
-
-bool getLuaNoSideEffect()
-{
- return g_noLuaSideEffect==true;
-}
-
-void resetLuaSideEffect()
-{
- g_noLuaSideEffect = boost::logic::indeterminate;
-}
-
-map<ComboAddress,int> filterScore(const map<ComboAddress, unsigned int,ComboAddress::addressOnlyLessThan >& counts,
- double delta, int rate)
-{
- std::multimap<unsigned int,ComboAddress> score;
- for(const auto& e : counts)
- score.insert({e.second, e.first});
-
- map<ComboAddress,int> ret;
-
- double lim = delta*rate;
- for(auto s = score.crbegin(); s != score.crend() && s->first > lim; ++s) {
- ret[s->second]=s->first;
- }
- return ret;
-}
-
-
-typedef std::function<void(const StatNode&, const StatNode::Stat&, const StatNode::Stat&)> statvisitor_t;
-
-static void statNodeRespRing(statvisitor_t visitor, unsigned int seconds)
-{
- struct timespec cutoff, now;
- gettime(&now);
- if (seconds) {
- cutoff = now;
- cutoff.tv_sec -= seconds;
- }
-
- std::lock_guard<std::mutex> lock(g_rings.respMutex);
-
- StatNode root;
- for(const auto& c : g_rings.respRing) {
- if (now < c.when)
- continue;
-
- if (seconds && c.when < cutoff)
- continue;
-
- root.submit(c.name, c.dh.rcode, c.requestor);
- }
- StatNode::Stat node;
-
- root.visit([&visitor](const StatNode* node_, const StatNode::Stat& self, const StatNode::Stat& children) {
- visitor(*node_, self, children);}, node);
-
-}
-
-vector<pair<unsigned int, std::unordered_map<string,string> > > getRespRing(boost::optional<int> rcode)
-{
- typedef std::unordered_map<string,string> entry_t;
- vector<pair<unsigned int, entry_t > > ret;
- std::lock_guard<std::mutex> lock(g_rings.respMutex);
-
- entry_t e;
- unsigned int count=1;
- for(const auto& c : g_rings.respRing) {
- if(rcode && (rcode.get() != c.dh.rcode))
- continue;
- e["qname"]=c.name.toString();
- e["rcode"]=std::to_string(c.dh.rcode);
- ret.push_back(std::make_pair(count,e));
- count++;
- }
- return ret;
-}
-
-typedef map<ComboAddress, unsigned int,ComboAddress::addressOnlyLessThan > counts_t;
-map<ComboAddress,int> exceedRespGen(int rate, int seconds, std::function<void(counts_t&, const Rings::Response&)> T)
-{
- counts_t counts;
- struct timespec cutoff, mintime, now;
- gettime(&now);
- cutoff = mintime = now;
- cutoff.tv_sec -= seconds;
-
- std::lock_guard<std::mutex> lock(g_rings.respMutex);
- for(const auto& c : g_rings.respRing) {
- if(seconds && c.when < cutoff)
- continue;
- if(now < c.when)
- continue;
-
- T(counts, c);
- if(c.when < mintime)
- mintime = c.when;
- }
- double delta = seconds ? seconds : DiffTime(now, mintime);
- return filterScore(counts, delta, rate);
-}
-
-map<ComboAddress,int> exceedQueryGen(int rate, int seconds, std::function<void(counts_t&, const Rings::Query&)> T)
-{
- counts_t counts;
- struct timespec cutoff, mintime, now;
- gettime(&now);
- cutoff = mintime = now;
- cutoff.tv_sec -= seconds;
-
- ReadLock rl(&g_rings.queryLock);
- for(const auto& c : g_rings.queryRing) {
- if(seconds && c.when < cutoff)
- continue;
- if(now < c.when)
- continue;
- T(counts, c);
- if(c.when < mintime)
- mintime = c.when;
- }
- double delta = seconds ? seconds : DiffTime(now, mintime);
- return filterScore(counts, delta, rate);
-}
-
-
-map<ComboAddress,int> exceedRCode(int rate, int seconds, int rcode)
-{
- return exceedRespGen(rate, seconds, [rcode](counts_t& counts, const Rings::Response& r)
- {
- if(r.dh.rcode == rcode)
- counts[r.requestor]++;
- });
-}
-
-map<ComboAddress,int> exceedRespByterate(int rate, int seconds)
-{
- return exceedRespGen(rate, seconds, [](counts_t& counts, const Rings::Response& r)
- {
- counts[r.requestor]+=r.size;
- });
-}
-
-#ifdef HAVE_DNSCRYPT
-static bool generateDNSCryptCertificate(const std::string& providerPrivateKeyFile, uint32_t serial, time_t begin, time_t end, DnsCryptCert& certOut, DnsCryptPrivateKey& keyOut)
-{
- bool success = false;
- unsigned char providerPrivateKey[DNSCRYPT_PROVIDER_PRIVATE_KEY_SIZE];
- sodium_mlock(providerPrivateKey, sizeof(providerPrivateKey));
- sodium_memzero(providerPrivateKey, sizeof(providerPrivateKey));
-
- try {
- ifstream providerKStream(providerPrivateKeyFile);
- providerKStream.read((char*) providerPrivateKey, sizeof(providerPrivateKey));
- if (providerKStream.fail()) {
- providerKStream.close();
- throw std::runtime_error("Invalid DNSCrypt provider key file " + providerPrivateKeyFile);
- }
-
- DnsCryptContext::generateCertificate(serial, begin, end, providerPrivateKey, keyOut, certOut);
- success = true;
- }
- catch(const std::exception& e) {
- errlog(e.what());
- }
-
- sodium_memzero(providerPrivateKey, sizeof(providerPrivateKey));
- sodium_munlock(providerPrivateKey, sizeof(providerPrivateKey));
- return success;
-}
-#endif /* HAVE_DNSCRYPT */
-
-void moreLua(bool client)
-{
- typedef NetmaskTree<DynBlock> nmts_t;
- g_lua.writeFunction("newCA", [](const std::string& name) { return ComboAddress(name); });
-
- g_lua.writeFunction("newNMG", []() { return NetmaskGroup(); });
- g_lua.registerFunction<void(NetmaskGroup::*)(const std::string&mask)>("addMask", [](NetmaskGroup&nmg, const std::string& mask)
- {
- nmg.addMask(mask);
- });
- g_lua.registerFunction<void(NetmaskGroup::*)(const std::map<ComboAddress,int>& map)>("addMasks", [](NetmaskGroup&nmg, const std::map<ComboAddress,int>& map)
- {
- for (const auto& entry : map) {
- nmg.addMask(Netmask(entry.first));
- }
- });
-
- g_lua.registerFunction("match", (bool (NetmaskGroup::*)(const ComboAddress&) const)&NetmaskGroup::match);
- g_lua.registerFunction("size", &NetmaskGroup::size);
- g_lua.registerFunction("clear", &NetmaskGroup::clear);
-
-
- g_lua.writeFunction("showDynBlocks", []() {
- setLuaNoSideEffect();
- auto slow = g_dynblockNMG.getCopy();
- struct timespec now;
- gettime(&now);
- boost::format fmt("%-24s %8d %8d %s\n");
- g_outputBuffer = (fmt % "What" % "Seconds" % "Blocks" % "Reason").str();
- for(const auto& e: slow) {
- if(now < e->second.until)
- g_outputBuffer+= (fmt % e->first.toString() % (e->second.until.tv_sec - now.tv_sec) % e->second.blocks % e->second.reason).str();
- }
- auto slow2 = g_dynblockSMT.getCopy();
- slow2.visit([&now, &fmt](const SuffixMatchTree<DynBlock>& node) {
- if(now <node.d_value.until) {
- string dom("empty");
- if(!node.d_value.domain.empty())
- dom = node.d_value.domain.toString();
- g_outputBuffer+= (fmt % dom % (node.d_value.until.tv_sec - now.tv_sec) % node.d_value.blocks % node.d_value.reason).str();
- }
- });
-
- });
-
- g_lua.writeFunction("clearDynBlocks", []() {
- setLuaSideEffect();
- nmts_t nmg;
- g_dynblockNMG.setState(nmg);
- SuffixMatchTree<DynBlock> smt;
- g_dynblockSMT.setState(smt);
- });
-
- g_lua.writeFunction("addDynBlocks",
- [](const map<ComboAddress,int>& m, const std::string& msg, boost::optional<int> seconds, boost::optional<DNSAction::Action> action) {
- setLuaSideEffect();
- auto slow = g_dynblockNMG.getCopy();
- struct timespec until, now;
- gettime(&now);
- until=now;
- int actualSeconds = seconds ? *seconds : 10;
- until.tv_sec += actualSeconds;
- for(const auto& capair : m) {
- unsigned int count = 0;
- auto got = slow.lookup(Netmask(capair.first));
- bool expired=false;
- if(got) {
- if(until < got->second.until) // had a longer policy
- continue;
- if(now < got->second.until) // only inherit count on fresh query we are extending
- count=got->second.blocks;
- else
- expired=true;
- }
- DynBlock db{msg,until,DNSName(),(action ? *action : DNSAction::Action::None)};
- db.blocks=count;
- if(!got || expired)
- warnlog("Inserting dynamic block for %s for %d seconds: %s", capair.first.toString(), actualSeconds, msg);
- slow.insert(Netmask(capair.first)).second=db;
- }
- g_dynblockNMG.setState(slow);
- });
-
- g_lua.writeFunction("addDynBlockSMT",
- [](const vector<pair<unsigned int, string> >&names, const std::string& msg, boost::optional<int> seconds, boost::optional<DNSAction::Action> action) {
- setLuaSideEffect();
- auto slow = g_dynblockSMT.getCopy();
- struct timespec until, now;
- gettime(&now);
- until=now;
- int actualSeconds = seconds ? *seconds : 10;
- until.tv_sec += actualSeconds;
-
- for(const auto& capair : names) {
- unsigned int count = 0;
- DNSName domain(capair.second);
- auto got = slow.lookup(domain);
- bool expired=false;
- if(got) {
- if(until < got->until) // had a longer policy
- continue;
- if(now < got->until) // only inherit count on fresh query we are extending
- count=got->blocks;
- else
- expired=true;
- }
-
- DynBlock db{msg,until,domain,(action ? *action : DNSAction::Action::None)};
- db.blocks=count;
- if(!got || expired)
- warnlog("Inserting dynamic block for %s for %d seconds: %s", domain, actualSeconds, msg);
- slow.add(domain, db);
- }
- g_dynblockSMT.setState(slow);
- });
-
- g_lua.writeFunction("setDynBlocksAction", [](DNSAction::Action action) {
- if (!g_configurationDone) {
- if (action == DNSAction::Action::Drop || action == DNSAction::Action::Refused || action == DNSAction::Action::Truncate) {
- g_dynBlockAction = action;
- }
- else {
- errlog("Dynamic blocks action can only be Drop, Refused or Truncate!");
- g_outputBuffer="Dynamic blocks action can only be Drop, Refused or Truncate!\n";
- }
- } else {
- g_outputBuffer="Dynamic blocks action cannot be altered at runtime!\n";
- }
- });
-
- g_lua.registerFunction<bool(nmts_t::*)(const ComboAddress&)>("match",
- [](nmts_t& s, const ComboAddress& ca) { return s.match(ca); });
-
- g_lua.writeFunction("exceedServFails", [](unsigned int rate, int seconds) {
- setLuaNoSideEffect();
- return exceedRCode(rate, seconds, RCode::ServFail);
- });
- g_lua.writeFunction("exceedNXDOMAINs", [](unsigned int rate, int seconds) {
- setLuaNoSideEffect();
- return exceedRCode(rate, seconds, RCode::NXDomain);
- });
-
-
-
- g_lua.writeFunction("exceedRespByterate", [](unsigned int rate, int seconds) {
- setLuaNoSideEffect();
- return exceedRespByterate(rate, seconds);
- });
-
- g_lua.writeFunction("exceedQTypeRate", [](uint16_t type, unsigned int rate, int seconds) {
- setLuaNoSideEffect();
- return exceedQueryGen(rate, seconds, [type](counts_t& counts, const Rings::Query& q) {
- if(q.qtype==type)
- counts[q.requestor]++;
- });
- });
-
- g_lua.writeFunction("exceedQRate", [](unsigned int rate, int seconds) {
- setLuaNoSideEffect();
- return exceedQueryGen(rate, seconds, [](counts_t& counts, const Rings::Query& q) {
- counts[q.requestor]++;
- });
- });
-
- g_lua.writeFunction("getRespRing", getRespRing);
-
- g_lua.registerFunction<StatNode, unsigned int()>("numChildren",
- [](StatNode& sn) -> unsigned int {
-
- return sn.children.size();
- } );
-
- g_lua.registerMember("fullname", &StatNode::fullname);
- g_lua.registerMember("labelsCount", &StatNode::labelsCount);
- g_lua.registerMember("servfails", &StatNode::Stat::servfails);
- g_lua.registerMember("nxdomains", &StatNode::Stat::nxdomains);
- g_lua.registerMember("queries", &StatNode::Stat::queries);
-
- g_lua.writeFunction("statNodeRespRing", [](statvisitor_t visitor, boost::optional<unsigned int> seconds) {
- statNodeRespRing(visitor, seconds ? *seconds : 0);
- });
-
- g_lua.writeFunction("getTopBandwidth", [](unsigned int top) {
- setLuaNoSideEffect();
- return g_rings.getTopBandwidth(top);
- });
- g_lua.executeCode(R"(function topBandwidth(top) top = top or 10; for k,v in ipairs(getTopBandwidth(top)) do show(string.format("%4d %-40s %4d %4.1f%%",k,v[1],v[2],v[3])) end end)");
-
- g_lua.writeFunction("delta", []() {
- setLuaNoSideEffect();
- // we hold the lua lock already!
- for(const auto& d : g_confDelta) {
- struct tm tm;
- localtime_r(&d.first.tv_sec, &tm);
- char date[80];
- strftime(date, sizeof(date)-1, "-- %a %b %d %Y %H:%M:%S %Z\n", &tm);
- g_outputBuffer += date;
- g_outputBuffer += d.second + "\n";
- }
- });
-
- g_lua.writeFunction("grepq", [](boost::variant<string, vector<pair<int,string> > > inp, boost::optional<unsigned int> limit) {
- setLuaNoSideEffect();
- boost::optional<Netmask> nm;
- boost::optional<DNSName> dn;
- int msec=-1;
-
- vector<string> vec;
- auto str=boost::get<string>(&inp);
- if(str)
- vec.push_back(*str);
- else {
- auto v = boost::get<vector<pair<int, string> > >(inp);
- for(const auto& a: v)
- vec.push_back(a.second);
- }
-
- for(const auto& s : vec) {
- try
- {
- nm = Netmask(s);
- }
- catch(...) {
- if(boost::ends_with(s,"ms") && sscanf(s.c_str(), "%ums", &msec)) {
- ;
- }
- else {
- try { dn=DNSName(s); }
- catch(...)
- {
- g_outputBuffer = "Could not parse '"+s+"' as domain name or netmask";
- return;
- }
- }
- }
- }
-
- decltype(g_rings.queryRing) qr;
- decltype(g_rings.respRing) rr;
- {
- ReadLock rl(&g_rings.queryLock);
- qr=g_rings.queryRing;
- }
- sort(qr.begin(), qr.end(), [](const decltype(qr)::value_type& a, const decltype(qr)::value_type& b) {
- return b.when < a.when;
- });
- {
- std::lock_guard<std::mutex> lock(g_rings.respMutex);
- rr=g_rings.respRing;
- }
-
- sort(rr.begin(), rr.end(), [](const decltype(rr)::value_type& a, const decltype(rr)::value_type& b) {
- return b.when < a.when;
- });
-
- unsigned int num=0;
- struct timespec now;
- gettime(&now);
-
- std::multimap<struct timespec, string> out;
-
- boost::format fmt("%-7.1f %-47s %-12s %-5d %-25s %-5s %-6.1f %-2s %-2s %-2s %s\n");
- g_outputBuffer+= (fmt % "Time" % "Client" % "Server" % "ID" % "Name" % "Type" % "Lat." % "TC" % "RD" % "AA" % "Rcode").str();
-
- if(msec==-1) {
- for(const auto& c : qr) {
- bool nmmatch=true, dnmatch=true;
- if(nm)
- nmmatch = nm->match(c.requestor);
- if(dn)
- dnmatch = c.name.isPartOf(*dn);
- if(nmmatch && dnmatch) {
- QType qt(c.qtype);
- out.insert(make_pair(c.when, (fmt % DiffTime(now, c.when) % c.requestor.toStringWithPort() % "" % htons(c.dh.id) % c.name.toString() % qt.getName() % "" % (c.dh.tc ? "TC" : "") % (c.dh.rd? "RD" : "") % (c.dh.aa? "AA" : "") % "Question").str() )) ;
-
- if(limit && *limit==++num)
- break;
- }
- }
- }
- num=0;
-
-
- string extra;
- for(const auto& c : rr) {
- bool nmmatch=true, dnmatch=true, msecmatch=true;
- if(nm)
- nmmatch = nm->match(c.requestor);
- if(dn)
- dnmatch = c.name.isPartOf(*dn);
- if(msec != -1)
- msecmatch=(c.usec/1000 > (unsigned int)msec);
-
- if(nmmatch && dnmatch && msecmatch) {
- QType qt(c.qtype);
- if(!c.dh.rcode)
- extra=". " +std::to_string(htons(c.dh.ancount))+ " answers";
- else
- extra.clear();
- if(c.usec != std::numeric_limits<decltype(c.usec)>::max())
- out.insert(make_pair(c.when, (fmt % DiffTime(now, c.when) % c.requestor.toStringWithPort() % c.ds.toStringWithPort() % htons(c.dh.id) % c.name.toString() % qt.getName() % (c.usec/1000.0) % (c.dh.tc ? "TC" : "") % (c.dh.rd? "RD" : "") % (c.dh.aa? "AA" : "") % (RCode::to_s(c.dh.rcode) + extra)).str() )) ;
- else
- out.insert(make_pair(c.when, (fmt % DiffTime(now, c.when) % c.requestor.toStringWithPort() % c.ds.toStringWithPort() % htons(c.dh.id) % c.name.toString() % qt.getName() % "T.O" % (c.dh.tc ? "TC" : "") % (c.dh.rd? "RD" : "") % (c.dh.aa? "AA" : "") % (RCode::to_s(c.dh.rcode) + extra)).str() )) ;
-
- if(limit && *limit==++num)
- break;
- }
- }
-
- for(const auto& p : out) {
- g_outputBuffer+=p.second;
- }
- });
-
- g_lua.writeFunction("addDNSCryptBind", [](const std::string& addr, const std::string& providerName, const std::string& certFile, const std::string keyFile, boost::optional<localbind_t> vars) {
- if (g_configurationDone) {
- g_outputBuffer="addDNSCryptBind cannot be used at runtime!\n";
- return;
- }
-#ifdef HAVE_DNSCRYPT
- bool doTCP = true;
- bool reusePort = false;
- int tcpFastOpenQueueSize = 0;
- std::string interface;
- std::set<int> cpus;
-
- parseLocalBindVars(vars, doTCP, reusePort, tcpFastOpenQueueSize, interface, cpus);
-
- try {
- DnsCryptContext ctx(providerName, certFile, keyFile);
- g_dnsCryptLocals.push_back(std::make_tuple(ComboAddress(addr, 443), ctx, reusePort, tcpFastOpenQueueSize, interface, cpus));
- }
- catch(std::exception& e) {
- errlog(e.what());
- g_outputBuffer="Error: "+string(e.what())+"\n";
- }
-#else
- g_outputBuffer="Error: DNSCrypt support is not enabled.\n";
-#endif
- });
-
- g_lua.writeFunction("showDNSCryptBinds", []() {
- setLuaNoSideEffect();
-#ifdef HAVE_DNSCRYPT
- ostringstream ret;
- boost::format fmt("%1$-3d %2% %|25t|%3$-20.20s %|26t|%4$-8d %|35t|%5$-21.21s %|56t|%6$-9d %|66t|%7$-21.21s" );
- ret << (fmt % "#" % "Address" % "Provider Name" % "Serial" % "Validity" % "P. Serial" % "P. Validity") << endl;
- size_t idx = 0;
-
- for (const auto& local : g_dnsCryptLocals) {
- const DnsCryptContext& ctx = std::get<1>(local);
- bool const hasOldCert = ctx.hasOldCertificate();
- const DnsCryptCert& cert = ctx.getCurrentCertificate();
- const DnsCryptCert& oldCert = ctx.getOldCertificate();
-
- ret<< (fmt % idx % std::get<0>(local).toStringWithPort() % ctx.getProviderName() % cert.signedData.serial % DnsCryptContext::certificateDateToStr(cert.signedData.tsEnd) % (hasOldCert ? oldCert.signedData.serial : 0) % (hasOldCert ? DnsCryptContext::certificateDateToStr(oldCert.signedData.tsEnd) : "-")) << endl;
- idx++;
- }
-
- g_outputBuffer=ret.str();
-#else
- g_outputBuffer="Error: DNSCrypt support is not enabled.\n";
-#endif
- });
-
- g_lua.writeFunction("getDNSCryptBind", [client](size_t idx) {
- setLuaNoSideEffect();
-#ifdef HAVE_DNSCRYPT
- DnsCryptContext* ret = nullptr;
- if (idx < g_dnsCryptLocals.size()) {
- ret = &(std::get<1>(g_dnsCryptLocals.at(idx)));
- }
- return ret;
-#else
- g_outputBuffer="Error: DNSCrypt support is not enabled.\n";
-#endif
- });
-
-#ifdef HAVE_DNSCRYPT
- /* DnsCryptContext bindings */
- g_lua.registerFunction<std::string(DnsCryptContext::*)()>("getProviderName", [](const DnsCryptContext& ctx) { return ctx.getProviderName(); });
- g_lua.registerFunction<DnsCryptCert(DnsCryptContext::*)()>("getCurrentCertificate", [](const DnsCryptContext& ctx) { return ctx.getCurrentCertificate(); });
- g_lua.registerFunction<DnsCryptCert(DnsCryptContext::*)()>("getOldCertificate", [](const DnsCryptContext& ctx) { return ctx.getOldCertificate(); });
- g_lua.registerFunction("hasOldCertificate", &DnsCryptContext::hasOldCertificate);
- g_lua.registerFunction("loadNewCertificate", &DnsCryptContext::loadNewCertificate);
- g_lua.registerFunction<void(DnsCryptContext::*)(const std::string& providerPrivateKeyFile, uint32_t serial, time_t begin, time_t end)>("generateAndLoadInMemoryCertificate", [](DnsCryptContext& ctx, const std::string& providerPrivateKeyFile, uint32_t serial, time_t begin, time_t end) {
- DnsCryptPrivateKey privateKey;
- DnsCryptCert cert;
-
- try {
- if (generateDNSCryptCertificate(providerPrivateKeyFile, serial, begin, end, cert, privateKey)) {
- ctx.setNewCertificate(cert, privateKey);
- }
- }
- catch(const std::exception& e) {
- errlog(e.what());
- g_outputBuffer="Error: "+string(e.what())+"\n";
- }
- });
-
- /* DnsCryptCert */
- g_lua.registerFunction<std::string(DnsCryptCert::*)()>("getMagic", [](const DnsCryptCert& cert) { return std::string(reinterpret_cast<const char*>(cert.magic), sizeof(cert.magic)); });
- g_lua.registerFunction<std::string(DnsCryptCert::*)()>("getEsVersion", [](const DnsCryptCert& cert) { return std::string(reinterpret_cast<const char*>(cert.esVersion), sizeof(cert.esVersion)); });
- g_lua.registerFunction<std::string(DnsCryptCert::*)()>("getProtocolMinorVersion", [](const DnsCryptCert& cert) { return std::string(reinterpret_cast<const char*>(cert.protocolMinorVersion), sizeof(cert.protocolMinorVersion)); });
- g_lua.registerFunction<std::string(DnsCryptCert::*)()>("getSignature", [](const DnsCryptCert& cert) { return std::string(reinterpret_cast<const char*>(cert.signature), sizeof(cert.signature)); });
- g_lua.registerFunction<std::string(DnsCryptCert::*)()>("getResolverPublicKey", [](const DnsCryptCert& cert) { return std::string(reinterpret_cast<const char*>(cert.signedData.resolverPK), sizeof(cert.signedData.resolverPK)); });
- g_lua.registerFunction<std::string(DnsCryptCert::*)()>("getClientMagic", [](const DnsCryptCert& cert) { return std::string(reinterpret_cast<const char*>(cert.signedData.clientMagic), sizeof(cert.signedData.clientMagic)); });
- g_lua.registerFunction<uint32_t(DnsCryptCert::*)()>("getSerial", [](const DnsCryptCert& cert) { return cert.signedData.serial; });
- g_lua.registerFunction<uint32_t(DnsCryptCert::*)()>("getTSStart", [](const DnsCryptCert& cert) { return ntohl(cert.signedData.tsStart); });
- g_lua.registerFunction<uint32_t(DnsCryptCert::*)()>("getTSEnd", [](const DnsCryptCert& cert) { return ntohl(cert.signedData.tsEnd); });
-#endif
-
- g_lua.writeFunction("generateDNSCryptProviderKeys", [](const std::string& publicKeyFile, const std::string privateKeyFile) {
- setLuaNoSideEffect();
-#ifdef HAVE_DNSCRYPT
- unsigned char publicKey[DNSCRYPT_PROVIDER_PUBLIC_KEY_SIZE];
- unsigned char privateKey[DNSCRYPT_PROVIDER_PRIVATE_KEY_SIZE];
- sodium_mlock(privateKey, sizeof(privateKey));
-
- try {
- DnsCryptContext::generateProviderKeys(publicKey, privateKey);
-
- ofstream pubKStream(publicKeyFile);
- pubKStream.write((char*) publicKey, sizeof(publicKey));
- pubKStream.close();
-
- ofstream privKStream(privateKeyFile);
- privKStream.write((char*) privateKey, sizeof(privateKey));
- privKStream.close();
-
- g_outputBuffer="Provider fingerprint is: " + DnsCryptContext::getProviderFingerprint(publicKey) + "\n";
- }
- catch(std::exception& e) {
- errlog(e.what());
- g_outputBuffer="Error: "+string(e.what())+"\n";
- }
-
- sodium_memzero(privateKey, sizeof(privateKey));
- sodium_munlock(privateKey, sizeof(privateKey));
-#else
- g_outputBuffer="Error: DNSCrypt support is not enabled.\n";
-#endif
- });
-
- g_lua.writeFunction("printDNSCryptProviderFingerprint", [](const std::string& publicKeyFile) {
- setLuaNoSideEffect();
-#ifdef HAVE_DNSCRYPT
- unsigned char publicKey[DNSCRYPT_PROVIDER_PUBLIC_KEY_SIZE];
-
- try {
- ifstream file(publicKeyFile);
- file.read((char *) &publicKey, sizeof(publicKey));
-
- if (file.fail())
- throw std::runtime_error("Invalid dnscrypt provider public key file " + publicKeyFile);
-
- file.close();
- g_outputBuffer="Provider fingerprint is: " + DnsCryptContext::getProviderFingerprint(publicKey) + "\n";
- }
- catch(std::exception& e) {
- errlog(e.what());
- g_outputBuffer="Error: "+string(e.what())+"\n";
- }
-#else
- g_outputBuffer="Error: DNSCrypt support is not enabled.\n";
-#endif
- });
-
- g_lua.writeFunction("generateDNSCryptCertificate", [](const std::string& providerPrivateKeyFile, const std::string& certificateFile, const std::string privateKeyFile, uint32_t serial, time_t begin, time_t end) {
- setLuaNoSideEffect();
-#ifdef HAVE_DNSCRYPT
- DnsCryptPrivateKey privateKey;
- DnsCryptCert cert;
-
- try {
- if (generateDNSCryptCertificate(providerPrivateKeyFile, serial, begin, end, cert, privateKey)) {
- privateKey.saveToFile(privateKeyFile);
- DnsCryptContext::saveCertFromFile(cert, certificateFile);
- }
- }
- catch(const std::exception& e) {
- errlog(e.what());
- g_outputBuffer="Error: "+string(e.what())+"\n";
- }
-#else
- g_outputBuffer="Error: DNSCrypt support is not enabled.\n";
-#endif
- });
-
- g_lua.writeFunction("showPools", []() {
- setLuaNoSideEffect();
- try {
- ostringstream ret;
- boost::format fmt("%1$-20.20s %|25t|%2$20s %|25t|%3$20s %|50t|%4%" );
- // 1 2 3 4
- ret << (fmt % "Name" % "Cache" % "ServerPolicy" % "Servers" ) << endl;
-
- const auto localPools = g_pools.getCopy();
- for (const auto& entry : localPools) {
- const string& name = entry.first;
- const std::shared_ptr<ServerPool> pool = entry.second;
- string cache = pool->packetCache != nullptr ? pool->packetCache->toString() : "";
- string policy = g_policy.getLocal()->name;
- if (pool->policy != nullptr) {
- policy = pool->policy->name;
- }
- string servers;
-
- for (const auto& server: pool->servers) {
- if (!servers.empty()) {
- servers += ", ";
- }
- if (!server.second->name.empty()) {
- servers += server.second->name;
- servers += " ";
- }
- servers += server.second->remote.toStringWithPort();
- }
-
- ret << (fmt % name % cache % policy % servers) << endl;
- }
- g_outputBuffer=ret.str();
- }catch(std::exception& e) { g_outputBuffer=e.what(); throw; }
- });
-
- g_lua.registerFunction<void(std::shared_ptr<ServerPool>::*)(std::shared_ptr<DNSDistPacketCache>)>("setCache", [](std::shared_ptr<ServerPool> pool, std::shared_ptr<DNSDistPacketCache> cache) {
- if (pool) {
- pool->packetCache = cache;
- }
- });
- g_lua.registerFunction("getCache", &ServerPool::getCache);
- g_lua.registerFunction<void(std::shared_ptr<ServerPool>::*)()>("unsetCache", [](std::shared_ptr<ServerPool> pool) {
- if (pool) {
- pool->packetCache = nullptr;
- }
- });
-
- g_lua.writeFunction("newPacketCache", [client](size_t maxEntries, boost::optional<uint32_t> maxTTL, boost::optional<uint32_t> minTTL, boost::optional<uint32_t> tempFailTTL, boost::optional<uint32_t> staleTTL, boost::optional<bool> dontAge, boost::optional<size_t> numberOfShards, boost::optional<bool> deferrableInsertLock) {
- return std::make_shared<DNSDistPacketCache>(maxEntries, maxTTL ? *maxTTL : 86400, minTTL ? *minTTL : 0, tempFailTTL ? *tempFailTTL : 60, staleTTL ? *staleTTL : 60, dontAge ? *dontAge : false, numberOfShards ? *numberOfShards : 1, deferrableInsertLock ? *deferrableInsertLock : true);
- });
- g_lua.registerFunction("toString", &DNSDistPacketCache::toString);
- g_lua.registerFunction("isFull", &DNSDistPacketCache::isFull);
- g_lua.registerFunction("purgeExpired", &DNSDistPacketCache::purgeExpired);
- g_lua.registerFunction("expunge", &DNSDistPacketCache::expunge);
- g_lua.registerFunction<void(std::shared_ptr<DNSDistPacketCache>::*)(const DNSName& dname, boost::optional<uint16_t> qtype, boost::optional<bool> suffixMatch)>("expungeByName", [](
- std::shared_ptr<DNSDistPacketCache> cache,
- const DNSName& dname,
- boost::optional<uint16_t> qtype,
- boost::optional<bool> suffixMatch) {
- if (cache) {
- cache->expungeByName(dname, qtype ? *qtype : QType::ANY, suffixMatch ? *suffixMatch : false);
- }
- });
- g_lua.registerFunction<void(std::shared_ptr<DNSDistPacketCache>::*)()>("printStats", [](const std::shared_ptr<DNSDistPacketCache> cache) {
- if (cache) {
- g_outputBuffer="Entries: " + std::to_string(cache->getEntriesCount()) + "/" + std::to_string(cache->getMaxEntries()) + "\n";
- g_outputBuffer+="Hits: " + std::to_string(cache->getHits()) + "\n";
- g_outputBuffer+="Misses: " + std::to_string(cache->getMisses()) + "\n";
- g_outputBuffer+="Deferred inserts: " + std::to_string(cache->getDeferredInserts()) + "\n";
- g_outputBuffer+="Deferred lookups: " + std::to_string(cache->getDeferredLookups()) + "\n";
- g_outputBuffer+="Lookup Collisions: " + std::to_string(cache->getLookupCollisions()) + "\n";
- g_outputBuffer+="Insert Collisions: " + std::to_string(cache->getInsertCollisions()) + "\n";
- g_outputBuffer+="TTL Too Shorts: " + std::to_string(cache->getTTLTooShorts()) + "\n";
- }
- });
-
- g_lua.writeFunction("getPool", [client](const string& poolName) {
- if (client) {
- return std::make_shared<ServerPool>();
- }
- auto localPools = g_pools.getCopy();
- std::shared_ptr<ServerPool> pool = createPoolIfNotExists(localPools, poolName);
- g_pools.setState(localPools);
- return pool;
- });
-
- g_lua.writeFunction("setVerboseHealthChecks", [](bool verbose) { g_verboseHealthChecks=verbose; });
- g_lua.writeFunction("setStaleCacheEntriesTTL", [](uint32_t ttl) { g_staleCacheEntriesTTL = ttl; });
-
- g_lua.writeFunction("DropResponseAction", []() {
- return std::shared_ptr<DNSResponseAction>(new DropResponseAction);
- });
-
- g_lua.writeFunction("AllowResponseAction", []() {
- return std::shared_ptr<DNSResponseAction>(new AllowResponseAction);
- });
-
- g_lua.writeFunction("DelayResponseAction", [](int msec) {
- return std::shared_ptr<DNSResponseAction>(new DelayResponseAction(msec));
- });
-
- g_lua.writeFunction("RemoteLogAction", [](std::shared_ptr<RemoteLogger> logger, boost::optional<std::function<void(const DNSQuestion&, DNSDistProtoBufMessage*)> > alterFunc) {
-#ifdef HAVE_PROTOBUF
- return std::shared_ptr<DNSAction>(new RemoteLogAction(logger, alterFunc));
-#else
- throw std::runtime_error("Protobuf support is required to use RemoteLogAction");
-#endif
- });
- g_lua.writeFunction("RemoteLogResponseAction", [](std::shared_ptr<RemoteLogger> logger, boost::optional<std::function<void(const DNSResponse&, DNSDistProtoBufMessage*)> > alterFunc, boost::optional<bool> includeCNAME) {
-#ifdef HAVE_PROTOBUF
- return std::shared_ptr<DNSResponseAction>(new RemoteLogResponseAction(logger, alterFunc, includeCNAME ? *includeCNAME : false));
-#else
- throw std::runtime_error("Protobuf support is required to use RemoteLogResponseAction");
-#endif
- });
-
- g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(std::string)>("setTag", [](DNSDistProtoBufMessage& message, const std::string& strValue) {
- message.addTag(strValue);
- });
-
- g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(vector<pair<int, string>>)>("setTagArray", [](DNSDistProtoBufMessage& message, const vector<pair<int, string>>&tags) {
- for (const auto& tag : tags) {
- message.addTag(tag.second);
- }
- });
-
- g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(boost::optional <time_t> sec, boost::optional <uint32_t> uSec)>("setProtobufResponseType",
- [](DNSDistProtoBufMessage& message, boost::optional <time_t> sec, boost::optional <uint32_t> uSec) {
- message.setType(DNSProtoBufMessage::Response);
- message.setQueryTime(sec?*sec:0, uSec?*uSec:0);
- });
-
- g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(const std::string& strQueryName, uint16_t uType, uint16_t uClass, uint32_t uTTL, const std::string& strBlob)>("addResponseRR", [](DNSDistProtoBufMessage& message,
- const std::string& strQueryName, uint16_t uType, uint16_t uClass, uint32_t uTTL, const std::string& strBlob) {
- message.addRR(DNSName(strQueryName), uType, uClass, uTTL, strBlob);
- });
-
- g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(const Netmask&)>("setEDNSSubnet", [](DNSDistProtoBufMessage& message, const Netmask& subnet) { message.setEDNSSubnet(subnet); });
- g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(const DNSName&, uint16_t, uint16_t)>("setQuestion", [](DNSDistProtoBufMessage& message, const DNSName& qname, uint16_t qtype, uint16_t qclass) { message.setQuestion(qname, qtype, qclass); });
- g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(size_t)>("setBytes", [](DNSDistProtoBufMessage& message, size_t bytes) { message.setBytes(bytes); });
- g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(time_t, uint32_t)>("setTime", [](DNSDistProtoBufMessage& message, time_t sec, uint32_t usec) { message.setTime(sec, usec); });
- g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(time_t, uint32_t)>("setQueryTime", [](DNSDistProtoBufMessage& message, time_t sec, uint32_t usec) { message.setQueryTime(sec, usec); });
- g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(uint8_t)>("setResponseCode", [](DNSDistProtoBufMessage& message, uint8_t rcode) { message.setResponseCode(rcode); });
- g_lua.registerFunction<std::string(DNSDistProtoBufMessage::*)()>("toDebugString", [](const DNSDistProtoBufMessage& message) { return message.toDebugString(); });
-
- g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(const ComboAddress&)>("setRequestor", [](DNSDistProtoBufMessage& message, const ComboAddress& addr) {
- message.setRequestor(addr);
- });
- g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(const std::string&)>("setRequestorFromString", [](DNSDistProtoBufMessage& message, const std::string& str) {
- message.setRequestor(str);
- });
- g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(const ComboAddress&)>("setResponder", [](DNSDistProtoBufMessage& message, const ComboAddress& addr) {
- message.setResponder(addr);
- });
- g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(const std::string&)>("setResponderFromString", [](DNSDistProtoBufMessage& message, const std::string& str) {
- message.setResponder(str);
- });
-
- g_lua.writeFunction("newRemoteLogger", [client](const std::string& remote, boost::optional<uint16_t> timeout, boost::optional<uint64_t> maxQueuedEntries, boost::optional<uint8_t> reconnectWaitTime) {
- if (client) {
- return std::shared_ptr<RemoteLogger>();
- }
- return std::make_shared<RemoteLogger>(ComboAddress(remote), timeout ? *timeout : 2, maxQueuedEntries ? *maxQueuedEntries : 100, reconnectWaitTime ? *reconnectWaitTime : 1);
- });
-
- g_lua.writeFunction("TeeAction", [](const std::string& remote, boost::optional<bool> addECS) {
- return std::shared_ptr<DNSAction>(new TeeAction(ComboAddress(remote, 53), addECS ? *addECS : false));
- });
-
- g_lua.writeFunction("ECSPrefixLengthAction", [](uint16_t v4PrefixLength, uint16_t v6PrefixLength) {
- return std::shared_ptr<DNSAction>(new ECSPrefixLengthAction(v4PrefixLength, v6PrefixLength));
- });
-
- g_lua.writeFunction("ECSOverrideAction", [](bool ecsOverride) {
- return std::shared_ptr<DNSAction>(new ECSOverrideAction(ecsOverride));
- });
-
- g_lua.writeFunction("DisableECSAction", []() {
- return std::shared_ptr<DNSAction>(new DisableECSAction());
- });
-
- g_lua.registerFunction<void(DNSAction::*)()>("printStats", [](const DNSAction& ta) {
- setLuaNoSideEffect();
- auto stats = ta.getStats();
- for(const auto& s : stats) {
- g_outputBuffer+=s.first+"\t";
- if((uint64_t)s.second == s.second)
- g_outputBuffer += std::to_string((uint64_t)s.second)+"\n";
- else
- g_outputBuffer += std::to_string(s.second)+"\n";
- }
- });
-
- g_lua.writeFunction("getAction", [](unsigned int num) {
- setLuaNoSideEffect();
- boost::optional<std::shared_ptr<DNSAction>> ret;
- auto rulactions = g_rulactions.getCopy();
- if(num < rulactions.size())
- ret=rulactions[num].second;
- return ret;
- });
-
- g_lua.registerFunction("getStats", &DNSAction::getStats);
-
- g_lua.writeFunction("addResponseAction", [](luadnsrule_t var, boost::variant<std::shared_ptr<DNSAction>, std::shared_ptr<DNSResponseAction> > era) {
- if (era.type() == typeid(std::shared_ptr<DNSAction>)) {
- throw std::runtime_error("addResponseAction() can only be called with response-related actions, not query-related ones. Are you looking for addAction()?");
- }
-
- auto ea = *boost::get<std::shared_ptr<DNSResponseAction>>(&era);
-
- setLuaSideEffect();
- auto rule=makeRule(var);
- g_resprulactions.modify([rule, ea](decltype(g_resprulactions)::value_type& rulactions){
- rulactions.push_back({rule, ea});
- });
- });
-
- g_lua.writeFunction("showResponseRules", []() {
- setLuaNoSideEffect();
- boost::format fmt("%-3d %9d %-50s %s\n");
- g_outputBuffer += (fmt % "#" % "Matches" % "Rule" % "Action").str();
- int num=0;
- for(const auto& lim : g_resprulactions.getCopy()) {
- string name = lim.first->toString();
- g_outputBuffer += (fmt % num % lim.first->d_matches % name % lim.second->toString()).str();
- ++num;
- }
- });
-
- g_lua.writeFunction("rmResponseRule", [](unsigned int num) {
- setLuaSideEffect();
- auto rules = g_resprulactions.getCopy();
- if(num >= rules.size()) {
- g_outputBuffer = "Error: attempt to delete non-existing rule\n";
- return;
- }
- rules.erase(rules.begin()+num);
- g_resprulactions.setState(rules);
- });
-
- g_lua.writeFunction("topResponseRule", []() {
- setLuaSideEffect();
- auto rules = g_resprulactions.getCopy();
- if(rules.empty())
- return;
- auto subject = *rules.rbegin();
- rules.erase(std::prev(rules.end()));
- rules.insert(rules.begin(), subject);
- g_resprulactions.setState(rules);
- });
-
- g_lua.writeFunction("mvResponseRule", [](unsigned int from, unsigned int to) {
- setLuaSideEffect();
- auto rules = g_resprulactions.getCopy();
- if(from >= rules.size() || to > rules.size()) {
- g_outputBuffer = "Error: attempt to move rules from/to invalid index\n";
- return;
- }
- auto subject = rules[from];
- rules.erase(rules.begin()+from);
- if(to == rules.size())
- rules.push_back(subject);
- else {
- if(from < to)
- --to;
- rules.insert(rules.begin()+to, subject);
- }
- g_resprulactions.setState(rules);
- });
-
- g_lua.writeFunction("addCacheHitResponseAction", [](luadnsrule_t var, std::shared_ptr<DNSResponseAction> ea) {
- setLuaSideEffect();
- auto rule=makeRule(var);
- g_cachehitresprulactions.modify([rule, ea](decltype(g_cachehitresprulactions)::value_type& rulactions){
- rulactions.push_back({rule, ea});
- });
- });
-
- g_lua.writeFunction("showCacheHitResponseRules", []() {
- setLuaNoSideEffect();
- boost::format fmt("%-3d %9d %-50s %s\n");
- g_outputBuffer += (fmt % "#" % "Matches" % "Rule" % "Action").str();
- int num=0;
- for(const auto& lim : g_cachehitresprulactions.getCopy()) {
- string name = lim.first->toString();
- g_outputBuffer += (fmt % num % lim.first->d_matches % name % lim.second->toString()).str();
- ++num;
- }
- });
-
- g_lua.writeFunction("rmCacheHitResponseRule", [](unsigned int num) {
- setLuaSideEffect();
- auto rules = g_cachehitresprulactions.getCopy();
- if(num >= rules.size()) {
- g_outputBuffer = "Error: attempt to delete non-existing rule\n";
- return;
- }
- rules.erase(rules.begin()+num);
- g_cachehitresprulactions.setState(rules);
- });
-
- g_lua.writeFunction("topCacheHitResponseRule", []() {
- setLuaSideEffect();
- auto rules = g_cachehitresprulactions.getCopy();
- if(rules.empty())
- return;
- auto subject = *rules.rbegin();
- rules.erase(std::prev(rules.end()));
- rules.insert(rules.begin(), subject);
- g_cachehitresprulactions.setState(rules);
- });
-
- g_lua.writeFunction("mvCacheHitResponseRule", [](unsigned int from, unsigned int to) {
- setLuaSideEffect();
- auto rules = g_cachehitresprulactions.getCopy();
- if(from >= rules.size() || to > rules.size()) {
- g_outputBuffer = "Error: attempt to move rules from/to invalid index\n";
- return;
- }
- auto subject = rules[from];
- rules.erase(rules.begin()+from);
- if(to == rules.size())
- rules.push_back(subject);
- else {
- if(from < to)
- --to;
- rules.insert(rules.begin()+to, subject);
- }
- g_cachehitresprulactions.setState(rules);
- });
-
- g_lua.writeFunction("showBinds", []() {
- setLuaNoSideEffect();
- try {
- ostringstream ret;
- boost::format fmt("%1$-3d %2$-20.20s %|25t|%3$-8.8s %|35t|%4%" );
- // 1 2 3 4
- ret << (fmt % "#" % "Address" % "Protocol" % "Queries" ) << endl;
-
- size_t counter = 0;
- for (const auto& front : g_frontends) {
- ret << (fmt % counter % front->local.toStringWithPort() % (front->udpFD != -1 ? "UDP" : "TCP") % front->queries) << endl;
- counter++;
- }
- g_outputBuffer=ret.str();
- }catch(std::exception& e) { g_outputBuffer=e.what(); throw; }
- });
-
- g_lua.writeFunction("getBind", [](size_t num) {
- setLuaNoSideEffect();
- ClientState* ret = nullptr;
- if(num < g_frontends.size()) {
- ret=g_frontends[num];
- }
- return ret;
- });
-
- g_lua.registerFunction<std::string(ClientState::*)()>("toString", [](const ClientState& fe) {
- setLuaNoSideEffect();
- return fe.local.toStringWithPort();
- });
-
- g_lua.registerMember("muted", &ClientState::muted);
-
- g_lua.writeFunction("help", [](boost::optional<std::string> command) {
- setLuaNoSideEffect();
- g_outputBuffer = "";
- for (const auto& keyword : g_consoleKeywords) {
- if (!command) {
- g_outputBuffer += keyword.toString() + "\n";
- }
- else if (keyword.name == command) {
- g_outputBuffer = keyword.toString() + "\n";
- return;
- }
- }
- if (command) {
- g_outputBuffer = "Nothing found for " + *command + "\n";
- }
- });
-
- g_lua.writeFunction("showVersion", []() {
- setLuaNoSideEffect();
- g_outputBuffer = "dnsdist " + std::string(VERSION) + "\n";
- });
-
-#ifdef HAVE_EBPF
- g_lua.writeFunction("newBPFFilter", [client](uint32_t maxV4, uint32_t maxV6, uint32_t maxQNames) {
- if (client) {
- return std::shared_ptr<BPFFilter>(nullptr);
- }
- return std::make_shared<BPFFilter>(maxV4, maxV6, maxQNames);
- });
-
- g_lua.registerFunction<void(std::shared_ptr<BPFFilter>::*)(const ComboAddress& ca)>("block", [](std::shared_ptr<BPFFilter> bpf, const ComboAddress& ca) {
- if (bpf) {
- return bpf->block(ca);
- }
- });
-
- g_lua.registerFunction<void(std::shared_ptr<BPFFilter>::*)(const DNSName& qname, boost::optional<uint16_t> qtype)>("blockQName", [](std::shared_ptr<BPFFilter> bpf, const DNSName& qname, boost::optional<uint16_t> qtype) {
- if (bpf) {
- return bpf->block(qname, qtype ? *qtype : 255);
- }
- });
-
- g_lua.registerFunction<void(std::shared_ptr<BPFFilter>::*)(const ComboAddress& ca)>("unblock", [](std::shared_ptr<BPFFilter> bpf, const ComboAddress& ca) {
- if (bpf) {
- return bpf->unblock(ca);
- }
- });
-
- g_lua.registerFunction<void(std::shared_ptr<BPFFilter>::*)(const DNSName& qname, boost::optional<uint16_t> qtype)>("unblockQName", [](std::shared_ptr<BPFFilter> bpf, const DNSName& qname, boost::optional<uint16_t> qtype) {
- if (bpf) {
- return bpf->unblock(qname, qtype ? *qtype : 255);
- }
- });
-
- g_lua.registerFunction<std::string(std::shared_ptr<BPFFilter>::*)()>("getStats", [](const std::shared_ptr<BPFFilter> bpf) {
- setLuaNoSideEffect();
- std::string res;
- if (bpf) {
- std::vector<std::pair<ComboAddress, uint64_t> > stats = bpf->getAddrStats();
- for (const auto& value : stats) {
- if (value.first.sin4.sin_family == AF_INET) {
- res += value.first.toString() + ": " + std::to_string(value.second) + "\n";
- }
- else if (value.first.sin4.sin_family == AF_INET6) {
- res += "[" + value.first.toString() + "]: " + std::to_string(value.second) + "\n";
- }
- }
- std::vector<std::tuple<DNSName, uint16_t, uint64_t> > qstats = bpf->getQNameStats();
- for (const auto& value : qstats) {
- res += std::get<0>(value).toString() + " " + std::to_string(std::get<1>(value)) + ": " + std::to_string(std::get<2>(value)) + "\n";
- }
- }
- return res;
- });
-
- g_lua.registerFunction<void(std::shared_ptr<BPFFilter>::*)()>("attachToAllBinds", [](std::shared_ptr<BPFFilter> bpf) {
- std::string res;
- if (bpf) {
- for (const auto& frontend : g_frontends) {
- frontend->attachFilter(bpf);
- }
- }
- });
-
- g_lua.registerFunction<void(ClientState::*)()>("detachFilter", [](ClientState& frontend) {
- frontend.detachFilter();
- });
-
- g_lua.registerFunction<void(ClientState::*)(std::shared_ptr<BPFFilter>)>("attachFilter", [](ClientState& frontend, std::shared_ptr<BPFFilter> bpf) {
- if (bpf) {
- frontend.attachFilter(bpf);
- }
- });
-
- g_lua.writeFunction("setDefaultBPFFilter", [](std::shared_ptr<BPFFilter> bpf) {
- if (g_configurationDone) {
- g_outputBuffer="setDefaultBPFFilter() cannot be used at runtime!\n";
- return;
- }
- g_defaultBPFFilter = bpf;
- });
-
- g_lua.writeFunction("newDynBPFFilter", [client](std::shared_ptr<BPFFilter> bpf) {
- if (client) {
- return std::shared_ptr<DynBPFFilter>(nullptr);
- }
- return std::make_shared<DynBPFFilter>(bpf);
- });
-
- g_lua.writeFunction("registerDynBPFFilter", [](std::shared_ptr<DynBPFFilter> dbpf) {
- if (dbpf) {
- g_dynBPFFilters.push_back(dbpf);
- }
- });
-
- g_lua.writeFunction("unregisterDynBPFFilter", [](std::shared_ptr<DynBPFFilter> dbpf) {
- if (dbpf) {
- for (auto it = g_dynBPFFilters.begin(); it != g_dynBPFFilters.end(); it++) {
- if (*it == dbpf) {
- g_dynBPFFilters.erase(it);
- break;
- }
- }
- }
- });
-
- g_lua.registerFunction<void(std::shared_ptr<DynBPFFilter>::*)(const ComboAddress& addr, boost::optional<int> seconds)>("block", [](std::shared_ptr<DynBPFFilter> dbpf, const ComboAddress& addr, boost::optional<int> seconds) {
- if (dbpf) {
- struct timespec until;
- clock_gettime(CLOCK_MONOTONIC, &until);
- until.tv_sec += seconds ? *seconds : 10;
- dbpf->block(addr, until);
- }
- });
-
- g_lua.registerFunction<void(std::shared_ptr<DynBPFFilter>::*)()>("purgeExpired", [](std::shared_ptr<DynBPFFilter> dbpf) {
- if (dbpf) {
- struct timespec now;
- clock_gettime(CLOCK_MONOTONIC, &now);
- dbpf->purgeExpired(now);
- }
- });
-
- g_lua.writeFunction("addBPFFilterDynBlocks", [](const map<ComboAddress,int>& m, std::shared_ptr<DynBPFFilter> dynbpf, boost::optional<int> seconds) {
- setLuaSideEffect();
- struct timespec until, now;
- clock_gettime(CLOCK_MONOTONIC, &now);
- until=now;
- int actualSeconds = seconds ? *seconds : 10;
- until.tv_sec += actualSeconds;
- for(const auto& capair : m) {
- dynbpf->block(capair.first, until);
- }
- });
-
-#endif /* HAVE_EBPF */
-
- g_lua.writeFunction<std::unordered_map<string,uint64_t>()>("getStatisticsCounters", []() {
- setLuaNoSideEffect();
- std::unordered_map<string,uint64_t> res;
- for(const auto& entry : g_stats.entries) {
- if(const auto& val = boost::get<DNSDistStats::stat_t*>(&entry.second))
- res[entry.first] = (*val)->load();
- }
- return res;
- });
-
- g_lua.writeFunction("includeDirectory", [](const std::string& dirname) {
- if (g_configurationDone) {
- errlog("includeDirectory() cannot be used at runtime!");
- g_outputBuffer="includeDirectory() cannot be used at runtime!\n";
- return;
- }
-
- if (g_included) {
- errlog("includeDirectory() cannot be used recursively!");
- g_outputBuffer="includeDirectory() cannot be used recursively!\n";
- return;
- }
-
- g_included = true;
- struct stat st;
-
- if (stat(dirname.c_str(), &st)) {
- errlog("The included directory %s does not exist!", dirname.c_str());
- g_outputBuffer="The included directory " + dirname + " does not exist!";
- return;
- }
-
- if (!S_ISDIR(st.st_mode)) {
- errlog("The included directory %s is not a directory!", dirname.c_str());
- g_outputBuffer="The included directory " + dirname + " is not a directory!";
- return;
- }
-
- DIR *dirp;
- struct dirent *ent;
- std::list<std::string> files;
- if (!(dirp = opendir(dirname.c_str()))) {
- errlog("Error opening the included directory %s!", dirname.c_str());
- g_outputBuffer="Error opening the included directory " + dirname + "!";
- return;
- }
-
- while((ent = readdir(dirp)) != NULL) {
- if (ent->d_name[0] == '.') {
- continue;
- }
-
- if (boost::ends_with(ent->d_name, ".conf")) {
- std::ostringstream namebuf;
- namebuf << dirname.c_str() << "/" << ent->d_name;
-
- if (stat(namebuf.str().c_str(), &st) || !S_ISREG(st.st_mode)) {
- continue;
- }
-
- files.push_back(namebuf.str());
- }
- }
-
- closedir(dirp);
- files.sort();
-
- for (auto file = files.begin(); file != files.end(); ++file) {
- std::ifstream ifs(*file);
- if (!ifs) {
- warnlog("Unable to read configuration from '%s'", *file);
- } else {
- vinfolog("Read configuration from '%s'", *file);
- }
-
- g_lua.executeCode(ifs);
- }
-
- g_included = false;
- });
-
- g_lua.writeFunction("setAPIWritable", [](bool writable, boost::optional<std::string> apiConfigDir) {
- setLuaSideEffect();
- g_apiReadWrite = writable;
- if (apiConfigDir) {
- if (!(*apiConfigDir).empty()) {
- g_apiConfigDirectory = *apiConfigDir;
- }
- else {
- errlog("The API configuration directory value cannot be empty!");
- g_outputBuffer="The API configuration directory value cannot be empty!";
- }
- }
- });
-
- g_lua.writeFunction("setServFailWhenNoServer", [](bool servfail) {
- setLuaSideEffect();
- g_servFailOnNoPolicy = servfail;
- });
-
- g_lua.writeFunction("setRingBuffersSize", [](size_t capacity) {
- setLuaSideEffect();
- if (g_configurationDone) {
- errlog("setRingBuffersSize() cannot be used at runtime!");
- g_outputBuffer="setRingBuffersSize() cannot be used at runtime!\n";
- return;
- }
- g_rings.setCapacity(capacity);
- });
-
- g_lua.writeFunction("RDRule", []() {
- return std::shared_ptr<DNSRule>(new RDRule());
- });
-
- g_lua.writeFunction("TimedIPSetRule", []() {
- return std::shared_ptr<TimedIPSetRule>(new TimedIPSetRule());
- });
-
- g_lua.registerFunction<void(std::shared_ptr<TimedIPSetRule>::*)()>("clear", [](std::shared_ptr<TimedIPSetRule> tisr) {
- tisr->clear();
- });
-
- g_lua.registerFunction<void(std::shared_ptr<TimedIPSetRule>::*)()>("cleanup", [](std::shared_ptr<TimedIPSetRule> tisr) {
- tisr->cleanup();
- });
-
- g_lua.registerFunction<void(std::shared_ptr<TimedIPSetRule>::*)(const ComboAddress& ca, int t)>("add", [](std::shared_ptr<TimedIPSetRule> tisr, const ComboAddress& ca, int t) {
- tisr->add(ca, time(0)+t);
- });
-
- g_lua.registerFunction<std::shared_ptr<DNSRule>(std::shared_ptr<TimedIPSetRule>::*)()>("slice", [](std::shared_ptr<TimedIPSetRule> tisr) {
- return std::dynamic_pointer_cast<DNSRule>(tisr);
- });
-
- g_lua.writeFunction("setWHashedPertubation", [](uint32_t pertub) {
- setLuaSideEffect();
- g_hashperturb = pertub;
- });
-
- g_lua.writeFunction("setTCPUseSinglePipe", [](bool flag) {
- if (g_configurationDone) {
- g_outputBuffer="setTCPUseSinglePipe() cannot be used at runtime!\n";
- return;
- }
- setLuaSideEffect();
- g_useTCPSinglePipe = flag;
- });
-
- g_lua.writeFunction("snmpAgent", [](bool enableTraps, boost::optional<std::string> masterSocket) {
-#ifdef HAVE_NET_SNMP
- if (g_configurationDone) {
- errlog("snmpAgent() cannot be used at runtime!");
- g_outputBuffer="snmpAgent() cannot be used at runtime!\n";
- return;
- }
-
- if (g_snmpEnabled) {
- errlog("snmpAgent() cannot be used twice!");
- g_outputBuffer="snmpAgent() cannot be used twice!\n";
- return;
- }
-
- g_snmpEnabled = true;
- g_snmpTrapsEnabled = enableTraps;
- g_snmpAgent = new DNSDistSNMPAgent("dnsdist", masterSocket ? *masterSocket : std::string());
-#else
- errlog("NET SNMP support is required to use snmpAgent()");
- g_outputBuffer="NET SNMP support is required to use snmpAgent()\n";
-#endif /* HAVE_NET_SNMP */
- });
-
- g_lua.writeFunction("SNMPTrapAction", [](boost::optional<std::string> reason) {
-#ifdef HAVE_NET_SNMP
- return std::shared_ptr<DNSAction>(new SNMPTrapAction(reason ? *reason : ""));
-#else
- throw std::runtime_error("NET SNMP support is required to use SNMPTrapAction()");
-#endif /* HAVE_NET_SNMP */
- });
-
- g_lua.writeFunction("SNMPTrapResponseAction", [](boost::optional<std::string> reason) {
-#ifdef HAVE_NET_SNMP
- return std::shared_ptr<DNSResponseAction>(new SNMPTrapResponseAction(reason ? *reason : ""));
-#else
- throw std::runtime_error("NET SNMP support is required to use SNMPTrapResponseAction()");
-#endif /* HAVE_NET_SNMP */
- });
-
- g_lua.writeFunction("sendCustomTrap", [](const std::string& str) {
-#ifdef HAVE_NET_SNMP
- if (g_snmpAgent && g_snmpTrapsEnabled) {
- g_snmpAgent->sendCustomTrap(str);
- }
-#endif /* HAVE_NET_SNMP */
- });
-
- g_lua.writeFunction("setPoolServerPolicy", [](ServerPolicy policy, string pool) {
- setLuaSideEffect();
- auto localPools = g_pools.getCopy();
- setPoolPolicy(localPools, pool, std::make_shared<ServerPolicy>(policy));
- g_pools.setState(localPools);
- });
-
- g_lua.writeFunction("setPoolServerPolicyLua", [](string name, policyfunc_t policy, string pool) {
- setLuaSideEffect();
- auto localPools = g_pools.getCopy();
- setPoolPolicy(localPools, pool, std::make_shared<ServerPolicy>(ServerPolicy{name, policy}));
- g_pools.setState(localPools);
- });
-
- g_lua.writeFunction("showPoolServerPolicy", [](string pool) {
- setLuaSideEffect();
- auto localPools = g_pools.getCopy();
- auto poolObj = getPool(localPools, pool);
- if (poolObj->policy == nullptr) {
- g_outputBuffer=g_policy.getLocal()->name+"\n";
- } else {
- g_outputBuffer=poolObj->policy->name+"\n";
- }
- });
-
- g_lua.writeFunction("setTCPDownstreamCleanupInterval", [](uint16_t interval) {
- setLuaSideEffect();
- g_downstreamTCPCleanupInterval = interval;
- });
-
- g_lua.writeFunction("setConsoleConnectionsLogging", [](bool enabled) {
- g_logConsoleConnections = enabled;
- });
-
- g_lua.writeFunction("setUDPMultipleMessagesVectorSize", [](size_t vSize) {
- if (g_configurationDone) {
- errlog("setUDPMultipleMessagesVectorSize() cannot be used at runtime!");
- g_outputBuffer="setUDPMultipleMessagesVectorSize() cannot be used at runtime!\n";
- return;
- }
-#if defined(HAVE_RECVMMSG) && defined(HAVE_SENDMMSG) && defined(MSG_WAITFORONE)
- setLuaSideEffect();
- g_udpVectorSize = vSize;
-#else
- errlog("recvmmsg() support is not available!");
- g_outputBuffer="recvmmsg support is not available!\n";
-#endif
- });
-
- g_lua.writeFunction("TagAction", [](std::string tag, std::string value) {
- return std::shared_ptr<DNSAction>(new TagAction(tag, value));
- });
-
- g_lua.writeFunction("TagResponseAction", [](std::string tag, std::string value) {
- return std::shared_ptr<DNSResponseAction>(new TagResponseAction(tag, value));
- });
-
- g_lua.writeFunction("TagRule", [](std::string tag, boost::optional<std::string> value) {
- return std::shared_ptr<DNSRule>(new TagRule(tag, value));
- });
-}
#include "delaypipe.hh"
#include <unistd.h>
#include "sodcrypto.hh"
-#include "dnsrulactions.hh"
+#include "dnsdist-lua.hh"
#include <grp.h>
#include <pwd.h>
#include "lock.hh"
#include <sys/resource.h>
#include "dnsdist-cache.hh"
#include "gettime.hh"
+#include "ednsoptions.hh"
#ifdef HAVE_SYSTEMD
#include <systemd/sd-daemon.h>
bool putMsgLen32(int fd, uint32_t len);
void* tcpAcceptorThread(void* p);
-void moreLua(bool client);
void doClient(ComboAddress server, const std::string& command);
void doConsole();
void controlClientThread(int fd, ComboAddress client);
dnsdist-dnscrypt.cc \
dnsdist-ecs.cc dnsdist-ecs.hh \
dnsdist-lua.hh dnsdist-lua.cc \
- dnsdist-lua2.cc \
+ dnsdist-lua-actions.cc \
+ dnsdist-lua-bindings.cc \
+ dnsdist-lua-bindings-dnsquestion.cc \
+ dnsdist-lua-inspection.cc \
+ dnsdist-lua-rules.cc \
+ dnsdist-lua-vars.cc \
dnsdist-protobuf.cc dnsdist-protobuf.hh \
dnsdist-rings.cc \
dnsdist-snmp.cc dnsdist-snmp.hh \
dnslabeltext.cc \
dnsname.cc dnsname.hh \
dnsparser.hh dnsparser.cc \
- dnsrulactions.cc dnsrulactions.hh \
dnswriter.cc dnswriter.hh \
dolog.hh \
ednsoptions.cc ednsoptions.hh \
--- /dev/null
+../dnsdist-lua-actions.cc
\ No newline at end of file
--- /dev/null
+../dnsdist-lua-bindings-dnsquestion.cc
\ No newline at end of file
--- /dev/null
+../dnsdist-lua-bindings.cc
\ No newline at end of file
--- /dev/null
+../dnsdist-lua-inspection.cc
\ No newline at end of file
--- /dev/null
+../dnsdist-lua-rules.cc
\ No newline at end of file
--- /dev/null
+../dnsdist-lua-vars.cc
\ No newline at end of file
+++ /dev/null
-../dnsdist-lua2.cc
\ No newline at end of file
+++ /dev/null
-/*
- * 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 "dnsrulactions.hh"
-#include <iostream>
-#include <boost/format.hpp>
-
-using namespace std;
-
-bool ProbaRule::matches(const DNSQuestion* dq) const
-{
- if(d_proba == 1.0)
- return true;
- double rnd = 1.0*random() / RAND_MAX;
- return rnd > (1.0 - d_proba);
-}
-
-string ProbaRule::toString() const
-{
- return "match with prob. " + (boost::format("%0.2f") % d_proba).str();
-}
-
-
-TeeAction::TeeAction(const ComboAddress& ca, bool addECS) : d_remote(ca), d_addECS(addECS)
-{
- d_fd=SSocket(d_remote.sin4.sin_family, SOCK_DGRAM, 0);
- SConnect(d_fd, d_remote);
- setNonBlocking(d_fd);
- d_worker=std::thread(std::bind(&TeeAction::worker, this));
-}
-
-TeeAction::~TeeAction()
-{
- d_pleaseQuit=true;
- close(d_fd);
- d_worker.join();
-}
-
-
-DNSAction::Action TeeAction::operator()(DNSQuestion* dq, string* ruleresult) const
-{
- if(dq->tcp) {
- d_tcpdrops++;
- }
- else {
- ssize_t res;
- d_queries++;
-
- if(d_addECS) {
- std::string query;
- uint16_t len = dq->len;
- bool ednsAdded = false;
- bool ecsAdded = false;
- query.reserve(dq->size);
- query.assign((char*) dq->dh, len);
-
- if (!handleEDNSClientSubnet((char*) query.c_str(), query.capacity(), dq->qname->wirelength(), &len, &ednsAdded, &ecsAdded, *dq->remote, dq->ecsOverride, dq->ecsPrefixLength)) {
- return DNSAction::Action::None;
- }
-
- res = send(d_fd, query.c_str(), len, 0);
- }
- else {
- res = send(d_fd, (char*)dq->dh, dq->len, 0);
- }
-
- if (res <= 0)
- d_senderrors++;
- }
- return DNSAction::Action::None;
-}
-
-string TeeAction::toString() const
-{
- return "tee to "+d_remote.toStringWithPort();
-}
-
-std::unordered_map<string,double> TeeAction::getStats() const
-{
- return {{"queries", d_queries},
- {"responses", d_responses},
- {"recv-errors", d_recverrors},
- {"send-errors", d_senderrors},
- {"noerrors", d_noerrors},
- {"nxdomains", d_nxdomains},
- {"refuseds", d_refuseds},
- {"servfails", d_servfails},
- {"other-rcode", d_otherrcode},
- {"tcp-drops", d_tcpdrops}
- };
-}
-
-void TeeAction::worker()
-{
- char packet[1500];
- int res=0;
- struct dnsheader* dh=(struct dnsheader*)packet;
- for(;;) {
- res=waitForData(d_fd, 0, 250000);
- if(d_pleaseQuit)
- break;
- if(res < 0) {
- usleep(250000);
- continue;
- }
- if(res==0)
- continue;
- res=recv(d_fd, packet, sizeof(packet), 0);
- if(res <= (int)sizeof(struct dnsheader))
- d_recverrors++;
- else if(res > 0)
- d_responses++;
-
- if(dh->rcode == RCode::NoError)
- d_noerrors++;
- else if(dh->rcode == RCode::ServFail)
- d_servfails++;
- else if(dh->rcode == RCode::NXDomain)
- d_nxdomains++;
- else if(dh->rcode == RCode::Refused)
- d_refuseds++;
- else if(dh->rcode == RCode::FormErr)
- d_formerrs++;
- else if(dh->rcode == RCode::NotImp)
- d_notimps++;
- }
-}
+++ /dev/null
-../dnsrulactions.hh
\ No newline at end of file
+++ /dev/null
-/*
- * 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-ecs.hh"
-#include "dnsname.hh"
-#include "dolog.hh"
-#include "ednsoptions.hh"
-#include "lock.hh"
-#include "remote_logger.hh"
-#include "dnsdist-protobuf.hh"
-#include "dnsparser.hh"
-
-class MaxQPSIPRule : public DNSRule
-{
-public:
- MaxQPSIPRule(unsigned int qps, unsigned int burst, unsigned int ipv4trunc=32, unsigned int ipv6trunc=64) :
- d_qps(qps), d_burst(burst), d_ipv4trunc(ipv4trunc), d_ipv6trunc(ipv6trunc)
- {
- pthread_rwlock_init(&d_lock, 0);
- }
-
- bool matches(const DNSQuestion* dq) const override
- {
- ComboAddress zeroport(*dq->remote);
- zeroport.sin4.sin_port=0;
- zeroport.truncate(zeroport.sin4.sin_family == AF_INET ? d_ipv4trunc : d_ipv6trunc);
- {
- ReadLock r(&d_lock);
- const auto iter = d_limits.find(zeroport);
- if (iter != d_limits.end()) {
- return !iter->second.check();
- }
- }
- {
- WriteLock w(&d_lock);
- auto iter = d_limits.find(zeroport);
- if(iter == d_limits.end()) {
- iter=d_limits.insert({zeroport,QPSLimiter(d_qps, d_burst)}).first;
- }
- return !iter->second.check();
- }
- }
-
- string toString() const override
- {
- return "IP (/"+std::to_string(d_ipv4trunc)+", /"+std::to_string(d_ipv6trunc)+") match for QPS over " + std::to_string(d_qps) + " burst "+ std::to_string(d_burst);
- }
-
-
-private:
- mutable pthread_rwlock_t d_lock;
- mutable std::map<ComboAddress, QPSLimiter> d_limits;
- unsigned int d_qps, d_burst, d_ipv4trunc, d_ipv6trunc;
-
-};
-
-class MaxQPSRule : public DNSRule
-{
-public:
- MaxQPSRule(unsigned int qps)
- : d_qps(qps, qps)
- {}
-
- MaxQPSRule(unsigned int qps, unsigned int burst)
- : d_qps(qps, burst)
- {}
-
-
- bool matches(const DNSQuestion* qd) const override
- {
- return d_qps.check();
- }
-
- string toString() const override
- {
- return "Max " + std::to_string(d_qps.getRate()) + " qps";
- }
-
-
-private:
- mutable QPSLimiter d_qps;
-};
-
-class NMGRule : public DNSRule
-{
-public:
- NMGRule(const NetmaskGroup& nmg) : d_nmg(nmg) {}
-protected:
- NetmaskGroup d_nmg;
-};
-
-class NetmaskGroupRule : public NMGRule
-{
-public:
- NetmaskGroupRule(const NetmaskGroup& nmg, bool src) : NMGRule(nmg)
- {
- d_src = src;
- }
- bool matches(const DNSQuestion* dq) const override
- {
- if(!d_src) {
- return d_nmg.match(*dq->local);
- }
- return d_nmg.match(*dq->remote);
- }
-
- string toString() const override
- {
- if(!d_src) {
- return "Dst: "+d_nmg.toString();
- }
- return "Src: "+d_nmg.toString();
- }
-private:
- bool d_src;
-};
-
-class TimedIPSetRule : public DNSRule, boost::noncopyable
-{
-private:
- struct IPv6 {
- IPv6(const ComboAddress& ca)
- {
- static_assert(sizeof(*this)==16, "IPv6 struct has wrong size");
- memcpy((char*)this, ca.sin6.sin6_addr.s6_addr, 16);
- }
- bool operator==(const IPv6& rhs) const
- {
- return a==rhs.a && b==rhs.b;
- }
- uint64_t a, b;
- };
-
-public:
- TimedIPSetRule()
- {
- pthread_rwlock_init(&d_lock4, 0);
- pthread_rwlock_init(&d_lock6, 0);
- }
- bool matches(const DNSQuestion* dq) const override
- {
- if(dq->remote->sin4.sin_family == AF_INET) {
- ReadLock rl(&d_lock4);
- auto fnd = d_ip4s.find(dq->remote->sin4.sin_addr.s_addr);
- if(fnd == d_ip4s.end()) {
- return false;
- }
- return time(0) < fnd->second;
- } else {
- ReadLock rl(&d_lock6);
- auto fnd = d_ip6s.find({*dq->remote});
- if(fnd == d_ip6s.end()) {
- return false;
- }
- return time(0) < fnd->second;
- }
- }
-
- void add(const ComboAddress& ca, time_t ttd)
- {
- // think twice before adding templates here
- if(ca.sin4.sin_family == AF_INET) {
- WriteLock rl(&d_lock4);
- auto res=d_ip4s.insert({ca.sin4.sin_addr.s_addr, ttd});
- if(!res.second && (time_t)res.first->second < ttd)
- res.first->second = (uint32_t)ttd;
- }
- else {
- WriteLock rl(&d_lock6);
- auto res=d_ip6s.insert({{ca}, ttd});
- if(!res.second && (time_t)res.first->second < ttd)
- res.first->second = (uint32_t)ttd;
- }
- }
-
- void remove(const ComboAddress& ca)
- {
- if(ca.sin4.sin_family == AF_INET) {
- WriteLock rl(&d_lock4);
- d_ip4s.erase(ca.sin4.sin_addr.s_addr);
- }
- else {
- WriteLock rl(&d_lock6);
- d_ip6s.erase({ca});
- }
- }
-
- void clear()
- {
- {
- WriteLock rl(&d_lock4);
- d_ip4s.clear();
- }
- WriteLock rl(&d_lock6);
- d_ip6s.clear();
- }
-
- void cleanup()
- {
- time_t now=time(0);
- {
- WriteLock rl(&d_lock4);
-
- for(auto iter = d_ip4s.begin(); iter != d_ip4s.end(); ) {
- if(iter->second < now)
- iter=d_ip4s.erase(iter);
- else
- ++iter;
- }
-
- }
-
- {
- WriteLock rl(&d_lock6);
-
- for(auto iter = d_ip6s.begin(); iter != d_ip6s.end(); ) {
- if(iter->second < now)
- iter=d_ip6s.erase(iter);
- else
- ++iter;
- }
-
- }
-
- }
-
- string toString() const override
- {
- time_t now=time(0);
- uint64_t count = 0;
- {
- ReadLock rl(&d_lock4);
- for(const auto& ip : d_ip4s)
- if(now < ip.second)
- ++count;
- }
- {
- ReadLock rl(&d_lock6);
- for(const auto& ip : d_ip6s)
- if(now < ip.second)
- ++count;
- }
-
- return "Src: "+std::to_string(count)+" ips";
- }
-private:
- struct IPv6Hash
- {
- std::size_t operator()(const IPv6& ip) const
- {
- auto ah=std::hash<uint64_t>{}(ip.a);
- auto bh=std::hash<uint64_t>{}(ip.b);
- return ah & (bh<<1);
- }
- };
- std::unordered_map<IPv6, time_t, IPv6Hash> d_ip6s;
- std::unordered_map<uint32_t, time_t> d_ip4s;
- mutable pthread_rwlock_t d_lock4;
- mutable pthread_rwlock_t d_lock6;
-};
-
-
-class AllRule : public DNSRule
-{
-public:
- AllRule() {}
- bool matches(const DNSQuestion* dq) const override
- {
- return true;
- }
-
- string toString() const override
- {
- return "All";
- }
-
-};
-
-
-class DNSSECRule : public DNSRule
-{
-public:
- DNSSECRule()
- {
-
- }
- bool matches(const DNSQuestion* dq) const override
- {
- return dq->dh->cd || (getEDNSZ((const char*)dq->dh, dq->len) & EDNS_HEADER_FLAG_DO); // turns out dig sets ad by default..
- }
-
- string toString() const override
- {
- return "DNSSEC";
- }
-};
-
-class AndRule : public DNSRule
-{
-public:
- AndRule(const vector<pair<int, shared_ptr<DNSRule> > >& rules)
- {
- for(const auto& r : rules)
- d_rules.push_back(r.second);
- }
-
- bool matches(const DNSQuestion* dq) const override
- {
- auto iter = d_rules.begin();
- for(; iter != d_rules.end(); ++iter)
- if(!(*iter)->matches(dq))
- break;
- return iter == d_rules.end();
- }
-
- string toString() const override
- {
- string ret;
- for(const auto& rule : d_rules) {
- if(!ret.empty())
- ret+= " && ";
- ret += "("+ rule->toString()+")";
- }
- return ret;
- }
-private:
-
- vector<std::shared_ptr<DNSRule> > d_rules;
-
-};
-
-
-class OrRule : public DNSRule
-{
-public:
- OrRule(const vector<pair<int, shared_ptr<DNSRule> > >& rules)
- {
- for(const auto& r : rules)
- d_rules.push_back(r.second);
- }
-
- bool matches(const DNSQuestion* dq) const override
- {
- auto iter = d_rules.begin();
- for(; iter != d_rules.end(); ++iter)
- if((*iter)->matches(dq))
- return true;
- return false;
- }
-
- string toString() const override
- {
- string ret;
- for(const auto& rule : d_rules) {
- if(!ret.empty())
- ret+= " || ";
- ret += "("+ rule->toString()+")";
- }
- return ret;
- }
-private:
-
- vector<std::shared_ptr<DNSRule> > d_rules;
-
-};
-
-
-class RegexRule : public DNSRule
-{
-public:
- RegexRule(const std::string& regex) : d_regex(regex), d_visual(regex)
- {
-
- }
- bool matches(const DNSQuestion* dq) const override
- {
- return d_regex.match(dq->qname->toStringNoDot());
- }
-
- string toString() const override
- {
- return "Regex: "+d_visual;
- }
-private:
- Regex d_regex;
- string d_visual;
-};
-
-#ifdef HAVE_RE2
-#include <re2/re2.h>
-class RE2Rule : public DNSRule
-{
-public:
- RE2Rule(const std::string& re2) : d_re2(re2, RE2::Latin1), d_visual(re2)
- {
-
- }
- bool matches(const DNSQuestion* dq) const override
- {
- return RE2::FullMatch(dq->qname->toStringNoDot(), d_re2);
- }
-
- string toString() const override
- {
- return "RE2 match: "+d_visual;
- }
-private:
- RE2 d_re2;
- string d_visual;
-};
-#endif
-
-
-class SuffixMatchNodeRule : public DNSRule
-{
-public:
- SuffixMatchNodeRule(const SuffixMatchNode& smn, bool quiet=false) : d_smn(smn), d_quiet(quiet)
- {
- }
- bool matches(const DNSQuestion* dq) const override
- {
- return d_smn.check(*dq->qname);
- }
- string toString() const override
- {
- if(d_quiet)
- return "qname==in-set";
- else
- return "qname in "+d_smn.toString();
- }
-private:
- SuffixMatchNode d_smn;
- bool d_quiet;
-};
-
-class QNameRule : public DNSRule
-{
-public:
- QNameRule(const DNSName& qname) : d_qname(qname)
- {
- }
- bool matches(const DNSQuestion* dq) const override
- {
- return d_qname==*dq->qname;
- }
- string toString() const override
- {
- return "qname=="+d_qname.toString();
- }
-private:
- DNSName d_qname;
-};
-
-
-class QTypeRule : public DNSRule
-{
-public:
- QTypeRule(uint16_t qtype) : d_qtype(qtype)
- {
- }
- bool matches(const DNSQuestion* dq) const override
- {
- return d_qtype == dq->qtype;
- }
- string toString() const override
- {
- QType qt(d_qtype);
- return "qtype=="+qt.getName();
- }
-private:
- uint16_t d_qtype;
-};
-
-class QClassRule : public DNSRule
-{
-public:
- QClassRule(uint16_t qclass) : d_qclass(qclass)
- {
- }
- bool matches(const DNSQuestion* dq) const override
- {
- return d_qclass == dq->qclass;
- }
- string toString() const override
- {
- return "qclass=="+std::to_string(d_qclass);
- }
-private:
- uint16_t d_qclass;
-};
-
-class OpcodeRule : public DNSRule
-{
-public:
- OpcodeRule(uint8_t opcode) : d_opcode(opcode)
- {
- }
- bool matches(const DNSQuestion* dq) const override
- {
- return d_opcode == dq->dh->opcode;
- }
- string toString() const override
- {
- return "opcode=="+std::to_string(d_opcode);
- }
-private:
- uint8_t d_opcode;
-};
-
-class TCPRule : public DNSRule
-{
-public:
- TCPRule(bool tcp): d_tcp(tcp)
- {
- }
- bool matches(const DNSQuestion* dq) const override
- {
- return dq->tcp == d_tcp;
- }
- string toString() const override
- {
- return (d_tcp ? "TCP" : "UDP");
- }
-private:
- bool d_tcp;
-};
-
-
-class NotRule : public DNSRule
-{
-public:
- NotRule(shared_ptr<DNSRule>& rule): d_rule(rule)
- {
- }
- bool matches(const DNSQuestion* dq) const override
- {
- return !d_rule->matches(dq);
- }
- string toString() const override
- {
- return "!("+ d_rule->toString()+")";
- }
-private:
- shared_ptr<DNSRule> d_rule;
-};
-
-class RecordsCountRule : public DNSRule
-{
-public:
- RecordsCountRule(uint8_t section, uint16_t minCount, uint16_t maxCount): d_minCount(minCount), d_maxCount(maxCount), d_section(section)
- {
- }
- bool matches(const DNSQuestion* dq) const override
- {
- uint16_t count = 0;
- switch(d_section) {
- case 0:
- count = ntohs(dq->dh->qdcount);
- break;
- case 1:
- count = ntohs(dq->dh->ancount);
- break;
- case 2:
- count = ntohs(dq->dh->nscount);
- break;
- case 3:
- count = ntohs(dq->dh->arcount);
- break;
- }
- return count >= d_minCount && count <= d_maxCount;
- }
- string toString() const override
- {
- string section;
- switch(d_section) {
- case 0:
- section = "QD";
- break;
- case 1:
- section = "AN";
- break;
- case 2:
- section = "NS";
- break;
- case 3:
- section = "AR";
- break;
- }
- return std::to_string(d_minCount) + " <= records in " + section + " <= "+ std::to_string(d_maxCount);
- }
-private:
- uint16_t d_minCount;
- uint16_t d_maxCount;
- uint8_t d_section;
-};
-
-class RecordsTypeCountRule : public DNSRule
-{
-public:
- RecordsTypeCountRule(uint8_t section, uint16_t type, uint16_t minCount, uint16_t maxCount): d_type(type), d_minCount(minCount), d_maxCount(maxCount), d_section(section)
- {
- }
- bool matches(const DNSQuestion* dq) const override
- {
- uint16_t count = 0;
- switch(d_section) {
- case 0:
- count = ntohs(dq->dh->qdcount);
- break;
- case 1:
- count = ntohs(dq->dh->ancount);
- break;
- case 2:
- count = ntohs(dq->dh->nscount);
- break;
- case 3:
- count = ntohs(dq->dh->arcount);
- break;
- }
- if (count < d_minCount) {
- return false;
- }
- count = getRecordsOfTypeCount(reinterpret_cast<const char*>(dq->dh), dq->len, d_section, d_type);
- return count >= d_minCount && count <= d_maxCount;
- }
- string toString() const override
- {
- string section;
- switch(d_section) {
- case 0:
- section = "QD";
- break;
- case 1:
- section = "AN";
- break;
- case 2:
- section = "NS";
- break;
- case 3:
- section = "AR";
- break;
- }
- return std::to_string(d_minCount) + " <= " + QType(d_type).getName() + " records in " + section + " <= "+ std::to_string(d_maxCount);
- }
-private:
- uint16_t d_type;
- uint16_t d_minCount;
- uint16_t d_maxCount;
- uint8_t d_section;
-};
-
-class TrailingDataRule : public DNSRule
-{
-public:
- TrailingDataRule()
- {
- }
- bool matches(const DNSQuestion* dq) const override
- {
- uint16_t length = getDNSPacketLength(reinterpret_cast<const char*>(dq->dh), dq->len);
- return length < dq->len;
- }
- string toString() const override
- {
- return "trailing data";
- }
-};
-
-class QNameLabelsCountRule : public DNSRule
-{
-public:
- QNameLabelsCountRule(unsigned int minLabelsCount, unsigned int maxLabelsCount): d_min(minLabelsCount), d_max(maxLabelsCount)
- {
- }
- bool matches(const DNSQuestion* dq) const override
- {
- unsigned int count = dq->qname->countLabels();
- return count < d_min || count > d_max;
- }
- string toString() const override
- {
- return "labels count < " + std::to_string(d_min) + " || labels count > " + std::to_string(d_max);
- }
-private:
- unsigned int d_min;
- unsigned int d_max;
-};
-
-class QNameWireLengthRule : public DNSRule
-{
-public:
- QNameWireLengthRule(size_t min, size_t max): d_min(min), d_max(max)
- {
- }
- bool matches(const DNSQuestion* dq) const override
- {
- size_t const wirelength = dq->qname->wirelength();
- return wirelength < d_min || wirelength > d_max;
- }
- string toString() const override
- {
- return "wire length < " + std::to_string(d_min) + " || wire length > " + std::to_string(d_max);
- }
-private:
- size_t d_min;
- size_t d_max;
-};
-
-class RCodeRule : public DNSRule
-{
-public:
- RCodeRule(int rcode) : d_rcode(rcode)
- {
- }
- bool matches(const DNSQuestion* dq) const override
- {
- return d_rcode == dq->dh->rcode;
- }
- string toString() const override
- {
- return "rcode=="+RCode::to_s(d_rcode);
- }
-private:
- int d_rcode;
-};
-
-class RDRule : public DNSRule
-{
-public:
- RDRule()
- {
- }
- bool matches(const DNSQuestion* dq) const override
- {
- return dq->dh->rd == 1;
- }
- string toString() const override
- {
- return "rd==1";
- }
-};
-
-
-class ProbaRule : public DNSRule
-{
-public:
- ProbaRule(double proba) : d_proba(proba)
- {
- }
- bool matches(const DNSQuestion* dq) const override;
- string toString() const override;
-private:
- double d_proba;
-};
-
-class TagRule : public DNSRule
-{
-public:
- TagRule(std::string tag, boost::optional<std::string> value) : d_value(value), d_tag(tag)
- {
- }
- bool matches(const DNSQuestion* dq) const override
- {
- if (dq->qTag == nullptr) {
- return false;
- }
-
- const auto got = dq->qTag->tagData.find(d_tag);
- if (got == dq->qTag->tagData.cend()) {
- return false;
- }
-
- if (!d_value) {
- return true;
- }
-
- return got->second == *d_value;
- }
-
- string toString() const override
- {
- return "tag '" + d_tag + "' is set" + (d_value ? (" to '" + *d_value + "'") : "");
- }
-
-private:
- boost::optional<std::string> d_value;
- std::string d_tag;
-};
-
-
-class DropAction : public DNSAction
-{
-public:
- DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
- {
- return Action::Drop;
- }
- string toString() const override
- {
- return "drop";
- }
-};
-
-class AllowAction : public DNSAction
-{
-public:
- DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
- {
- return Action::Allow;
- }
- string toString() const override
- {
- return "allow";
- }
-};
-
-
-class QPSAction : public DNSAction
-{
-public:
- QPSAction(int limit) : d_qps(limit, limit)
- {}
- DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
- {
- if(d_qps.check())
- return Action::None;
- else
- return Action::Drop;
- }
- string toString() const override
- {
- return "qps limit to "+std::to_string(d_qps.getRate());
- }
-private:
- QPSLimiter d_qps;
-};
-
-class DelayAction : public DNSAction
-{
-public:
- DelayAction(int msec) : d_msec(msec)
- {}
- DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
- {
- *ruleresult=std::to_string(d_msec);
- return Action::Delay;
- }
- string toString() const override
- {
- return "delay by "+std::to_string(d_msec)+ " msec";
- }
-private:
- int d_msec;
-};
-
-
-class TeeAction : public DNSAction
-{
-public:
- TeeAction(const ComboAddress& ca, bool addECS=false);
- ~TeeAction() override;
- DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override;
- string toString() const override;
- std::unordered_map<string, double> getStats() const override;
-
-private:
- ComboAddress d_remote;
- std::thread d_worker;
- void worker();
-
- int d_fd;
- mutable std::atomic<unsigned long> d_senderrors{0};
- unsigned long d_recverrors{0};
- mutable std::atomic<unsigned long> d_queries{0};
- unsigned long d_responses{0};
- unsigned long d_nxdomains{0};
- unsigned long d_servfails{0};
- unsigned long d_refuseds{0};
- unsigned long d_formerrs{0};
- unsigned long d_notimps{0};
- unsigned long d_noerrors{0};
- mutable unsigned long d_tcpdrops{0};
- unsigned long d_otherrcode{0};
- std::atomic<bool> d_pleaseQuit{false};
- bool d_addECS{false};
-};
-
-class PoolAction : public DNSAction
-{
-public:
- PoolAction(const std::string& pool) : d_pool(pool) {}
- DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
- {
- *ruleresult=d_pool;
- return Action::Pool;
- }
- string toString() const override
- {
- return "to pool "+d_pool;
- }
-
-private:
- string d_pool;
-};
-
-
-class QPSPoolAction : public DNSAction
-{
-public:
- QPSPoolAction(unsigned int limit, const std::string& pool) : d_qps(limit, limit), d_pool(pool) {}
- DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
- {
- if(d_qps.check()) {
- *ruleresult=d_pool;
- return Action::Pool;
- }
- else
- return Action::None;
- }
- string toString() const override
- {
- return "max " +std::to_string(d_qps.getRate())+" to pool "+d_pool;
- }
-
-private:
- QPSLimiter d_qps;
- string d_pool;
-};
-
-class RCodeAction : public DNSAction
-{
-public:
- RCodeAction(int rcode) : d_rcode(rcode) {}
- DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
- {
- dq->dh->rcode = d_rcode;
- dq->dh->qr = true; // for good measure
- return Action::HeaderModify;
- }
- string toString() const override
- {
- return "set rcode "+std::to_string(d_rcode);
- }
-
-private:
- int d_rcode;
-};
-
-class TCAction : public DNSAction
-{
-public:
- DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
- {
- return Action::Truncate;
- }
- string toString() const override
- {
- return "tc=1 answer";
- }
-};
-
-class SpoofAction : public DNSAction
-{
-public:
- SpoofAction(const vector<ComboAddress>& addrs) : d_addrs(addrs)
- {
- }
-
- SpoofAction(const string& cname): d_cname(cname) { }
-
- DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
- {
- uint16_t qtype = dq->qtype;
- // do we even have a response?
- if(d_cname.empty() && !std::count_if(d_addrs.begin(), d_addrs.end(), [qtype](const ComboAddress& a)
- {
- return (qtype == QType::ANY || ((a.sin4.sin_family == AF_INET && qtype == QType::A) ||
- (a.sin4.sin_family == AF_INET6 && qtype == QType::AAAA)));
- }))
- return Action::None;
-
- vector<ComboAddress> addrs;
- unsigned int totrdatalen=0;
- if (!d_cname.empty()) {
- qtype = QType::CNAME;
- totrdatalen += d_cname.toDNSString().size();
- } else {
- 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;
- 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);
- }
- }
-
- if(addrs.size() > 1)
- random_shuffle(addrs.begin(), addrs.end());
-
- unsigned int consumed=0;
- DNSName ignore((char*)dq->dh, dq->len, sizeof(dnsheader), false, 0, 0, &consumed);
-
- if (dq->size < (sizeof(dnsheader) + consumed + 4 + ((d_cname.empty() ? 0 : 1) + addrs.size())*12 /* recordstart */ + totrdatalen)) {
- return Action::None;
- }
-
- dq->len = sizeof(dnsheader) + consumed + 4; // there goes your EDNS
- char* dest = ((char*)dq->dh) + dq->len;
-
- dq->dh->qr = true; // for good measure
- dq->dh->ra = dq->dh->rd; // for good measure
- dq->dh->ad = false;
- dq->dh->ancount = 0;
- dq->dh->arcount = 0; // for now, forget about your EDNS, we're marching over it
-
- if(qtype == QType::CNAME) {
- string wireData = d_cname.toDNSString(); // Note! This doesn't do compression!
- const unsigned char recordstart[]={0xc0, 0x0c, // compressed name
- 0, (unsigned char) qtype,
- 0, QClass::IN, // IN
- 0, 0, 0, 60, // TTL
- 0, (unsigned char)wireData.length()};
- static_assert(sizeof(recordstart) == 12, "sizeof(recordstart) must be equal to 12, otherwise the above check is invalid");
-
- memcpy(dest, recordstart, sizeof(recordstart));
- dest += sizeof(recordstart);
- memcpy(dest, wireData.c_str(), wireData.length());
- dq->len += wireData.length() + sizeof(recordstart);
- dq->dh->ancount++;
- }
- else {
- for(const auto& addr : addrs) {
- unsigned char rdatalen = addr.sin4.sin_family == AF_INET ? sizeof(addr.sin4.sin_addr.s_addr) : sizeof(addr.sin6.sin6_addr.s6_addr);
- const unsigned char recordstart[]={0xc0, 0x0c, // compressed name
- 0, (unsigned char) (addr.sin4.sin_family == AF_INET ? QType::A : QType::AAAA),
- 0, QClass::IN, // IN
- 0, 0, 0, 60, // TTL
- 0, rdatalen};
- static_assert(sizeof(recordstart) == 12, "sizeof(recordstart) must be equal to 12, otherwise the above check is invalid");
-
- memcpy(dest, recordstart, sizeof(recordstart));
- dest += sizeof(recordstart);
-
- memcpy(dest,
- addr.sin4.sin_family == AF_INET ? (void*)&addr.sin4.sin_addr.s_addr : (void*)&addr.sin6.sin6_addr.s6_addr,
- rdatalen);
- dest += rdatalen;
- dq->len += rdatalen + sizeof(recordstart);
- dq->dh->ancount++;
- }
- }
-
- dq->dh->ancount = htons(dq->dh->ancount);
-
- return Action::HeaderModify;
- }
-
- string toString() const override
- {
- string ret = "spoof in ";
- if(!d_cname.empty()) {
- ret+=d_cname.toString()+ " ";
- } else {
- for(const auto& a : d_addrs)
- ret += a.toString()+" ";
- }
- return ret;
- }
-private:
- std::vector<ComboAddress> d_addrs;
- DNSName d_cname;
-};
-
-class MacAddrAction : public DNSAction
-{
-public:
- MacAddrAction(uint16_t code) : d_code(code)
- {}
- DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
- {
- if(dq->dh->arcount)
- return Action::None;
-
- string mac = getMACAddress(*dq->remote);
- if(mac.empty())
- return Action::None;
-
- string optRData;
- generateEDNSOption(d_code, mac, optRData);
-
- string res;
- generateOptRR(optRData, res);
-
- if ((dq->size - dq->len) < res.length())
- return Action::None;
-
- dq->dh->arcount = htons(1);
- char* dest = ((char*)dq->dh) + dq->len;
- memcpy(dest, res.c_str(), res.length());
- dq->len += res.length();
-
- return Action::None;
- }
- string toString() const override
- {
- return "add EDNS MAC (code="+std::to_string(d_code)+")";
- }
-private:
- uint16_t d_code{3};
-};
-
-class NoRecurseAction : public DNSAction
-{
-public:
- DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
- {
- dq->dh->rd = false;
- return Action::None;
- }
- string toString() const override
- {
- return "set rd=0";
- }
-};
-
-class LogAction : public DNSAction, public boost::noncopyable
-{
-public:
- LogAction() : d_fp(0)
- {
- }
- LogAction(const std::string& str, bool binary=true, bool append=false, bool buffered=true) : d_fname(str), d_binary(binary)
- {
- if(str.empty())
- return;
- if(append)
- d_fp = fopen(str.c_str(), "a+");
- else
- d_fp = fopen(str.c_str(), "w");
- if(!d_fp)
- throw std::runtime_error("Unable to open file '"+str+"' for logging: "+string(strerror(errno)));
- if(!buffered)
- setbuf(d_fp, 0);
- }
- ~LogAction() override
- {
- if(d_fp)
- fclose(d_fp);
- }
- DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
- {
- if(!d_fp) {
- vinfolog("Packet from %s for %s %s with id %d", dq->remote->toStringWithPort(), dq->qname->toString(), QType(dq->qtype).getName(), dq->dh->id);
- }
- else {
- if(d_binary) {
- string out = dq->qname->toDNSString();
- fwrite(out.c_str(), 1, out.size(), d_fp);
- fwrite((void*)&dq->qtype, 1, 2, d_fp);
- }
- else {
- fprintf(d_fp, "Packet from %s for %s %s with id %d\n", dq->remote->toStringWithPort().c_str(), dq->qname->toString().c_str(), QType(dq->qtype).getName().c_str(), dq->dh->id);
- }
- }
- return Action::None;
- }
- string toString() const override
- {
- if (!d_fname.empty()) {
- return "log to " + d_fname;
- }
- return "log";
- }
-private:
- string d_fname;
- FILE* d_fp{0};
- bool d_binary{true};
-};
-
-
-class DisableValidationAction : public DNSAction
-{
-public:
- DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
- {
- dq->dh->cd = true;
- return Action::None;
- }
- string toString() const override
- {
- return "set cd=1";
- }
-};
-
-class SkipCacheAction : public DNSAction
-{
-public:
- DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
- {
- dq->skipCache = true;
- return Action::None;
- }
- string toString() const override
- {
- return "skip cache";
- }
-};
-
-class ECSPrefixLengthAction : public DNSAction
-{
-public:
- ECSPrefixLengthAction(uint16_t v4Length, uint16_t v6Length) : d_v4PrefixLength(v4Length), d_v6PrefixLength(v6Length)
- {
- }
- DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
- {
- dq->ecsPrefixLength = dq->remote->sin4.sin_family == AF_INET ? d_v4PrefixLength : d_v6PrefixLength;
- return Action::None;
- }
- string toString() const override
- {
- return "set ECS prefix length to " + std::to_string(d_v4PrefixLength) + "/" + std::to_string(d_v6PrefixLength);
- }
-private:
- uint16_t d_v4PrefixLength;
- uint16_t d_v6PrefixLength;
-};
-
-class ECSOverrideAction : public DNSAction
-{
-public:
- ECSOverrideAction(bool ecsOverride) : d_ecsOverride(ecsOverride)
- {
- }
- DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
- {
- dq->ecsOverride = d_ecsOverride;
- return Action::None;
- }
- string toString() const override
- {
- return "set ECS override to " + std::to_string(d_ecsOverride);
- }
-private:
- bool d_ecsOverride;
-};
-
-
-class DisableECSAction : public DNSAction
-{
-public:
- DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
- {
- dq->useECS = false;
- return Action::None;
- }
- string toString() const override
- {
- return "disable ECS";
- }
-};
-
-class RemoteLogAction : public DNSAction, public boost::noncopyable
-{
-public:
- RemoteLogAction(std::shared_ptr<RemoteLogger> logger, boost::optional<std::function<void(const DNSQuestion&, DNSDistProtoBufMessage*)> > alterFunc): d_logger(logger), d_alterFunc(alterFunc)
- {
- }
- DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
- {
-#ifdef HAVE_PROTOBUF
- if (!dq->uniqueId) {
- dq->uniqueId = t_uuidGenerator();
- }
-
- DNSDistProtoBufMessage message(*dq);
- {
- if (d_alterFunc) {
- std::lock_guard<std::mutex> lock(g_luamutex);
- (*d_alterFunc)(*dq, &message);
- }
- }
- std::string data;
- message.serialize(data);
- d_logger->queueData(data);
-#endif /* HAVE_PROTOBUF */
- return Action::None;
- }
- string toString() const override
- {
- return "remote log to " + (d_logger ? d_logger->toString() : "");
- }
-private:
- std::shared_ptr<RemoteLogger> d_logger;
- boost::optional<std::function<void(const DNSQuestion&, DNSDistProtoBufMessage*)> > d_alterFunc;
-};
-
-class SNMPTrapAction : public DNSAction
-{
-public:
- SNMPTrapAction(const std::string& reason): d_reason(reason)
- {
- }
- DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
- {
- if (g_snmpAgent && g_snmpTrapsEnabled) {
- g_snmpAgent->sendDNSTrap(*dq, d_reason);
- }
-
- return Action::None;
- }
- string toString() const override
- {
- return "send SNMP trap";
- }
-private:
- std::string d_reason;
-};
-
-class RemoteLogResponseAction : public DNSResponseAction, public boost::noncopyable
-{
-public:
- RemoteLogResponseAction(std::shared_ptr<RemoteLogger> logger, boost::optional<std::function<void(const DNSResponse&, DNSDistProtoBufMessage*)> > alterFunc, bool includeCNAME): d_logger(logger), d_alterFunc(alterFunc), d_includeCNAME(includeCNAME)
- {
- }
- DNSResponseAction::Action operator()(DNSResponse* dr, string* ruleresult) const override
- {
-#ifdef HAVE_PROTOBUF
- if (!dr->uniqueId) {
- dr->uniqueId = t_uuidGenerator();
- }
-
- DNSDistProtoBufMessage message(*dr, d_includeCNAME);
- {
- if (d_alterFunc) {
- std::lock_guard<std::mutex> lock(g_luamutex);
- (*d_alterFunc)(*dr, &message);
- }
- }
- std::string data;
- message.serialize(data);
- d_logger->queueData(data);
-#endif /* HAVE_PROTOBUF */
- return Action::None;
- }
- string toString() const override
- {
- return "remote log response to " + (d_logger ? d_logger->toString() : "");
- }
-private:
- std::shared_ptr<RemoteLogger> d_logger;
- boost::optional<std::function<void(const DNSResponse&, DNSDistProtoBufMessage*)> > d_alterFunc;
- bool d_includeCNAME;
-};
-
-class DropResponseAction : public DNSResponseAction
-{
-public:
- DNSResponseAction::Action operator()(DNSResponse* dr, string* ruleresult) const override
- {
- return Action::Drop;
- }
- string toString() const override
- {
- return "drop";
- }
-};
-
-class AllowResponseAction : public DNSResponseAction
-{
-public:
- DNSResponseAction::Action operator()(DNSResponse* dr, string* ruleresult) const override
- {
- return Action::Allow;
- }
- string toString() const override
- {
- return "allow";
- }
-};
-
-class DelayResponseAction : public DNSResponseAction
-{
-public:
- DelayResponseAction(int msec) : d_msec(msec)
- {}
- DNSResponseAction::Action operator()(DNSResponse* dr, string* ruleresult) const override
- {
- *ruleresult=std::to_string(d_msec);
- return Action::Delay;
- }
- string toString() const override
- {
- return "delay by "+std::to_string(d_msec)+ " msec";
- }
-private:
- int d_msec;
-};
-
-class SNMPTrapResponseAction : public DNSResponseAction
-{
-public:
- SNMPTrapResponseAction(const std::string& reason): d_reason(reason)
- {
- }
- DNSResponseAction::Action operator()(DNSResponse* dr, string* ruleresult) const override
- {
- if (g_snmpAgent && g_snmpTrapsEnabled) {
- g_snmpAgent->sendDNSTrap(*dr, d_reason);
- }
-
- return Action::None;
- }
- string toString() const override
- {
- return "send SNMP trap";
- }
-private:
- std::string d_reason;
-};
-
-class TagAction : public DNSAction
-{
-public:
- TagAction(const std::string tag, std::string value): d_tag(tag), d_value(value)
- {
- }
- DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
- {
- if (dq->qTag == nullptr) {
- dq->qTag = std::make_shared<QTag>();
- }
-
- dq->qTag->add(d_tag, d_value);
-
- return Action::None;
- }
- string toString() const override
- {
- return "set tag '" + d_tag + "' to value '" + d_value + "'";
- }
-private:
- std::string d_tag;
- std::string d_value;
-};
-
-class TagResponseAction : public DNSResponseAction
-{
-public:
- TagResponseAction(const std::string tag, std::string value): d_tag(tag), d_value(value)
- {
- }
- DNSResponseAction::Action operator()(DNSResponse* dr, string* ruleresult) const override
- {
- if (dr->qTag == nullptr) {
- dr->qTag = std::make_shared<QTag>();
- }
-
- dr->qTag->add(d_tag, d_value);
-
- return Action::None;
- }
- string toString() const override
- {
- return "set tag '" + d_tag + "' to value '" + d_value + "'";
- }
-private:
- std::string d_tag;
- std::string d_value;
-};
class TestAdvancedRemoveRD(DNSDistTest):
_config_template = """
- addNoRecurseRule("norecurse.advanced.tests.powerdns.com.")
+ addAction("norecurse.advanced.tests.powerdns.com.", NoRecurseAction())
newServer{address="127.0.0.1:%s"}
"""
class TestAdvancedAddCD(DNSDistTest):
_config_template = """
- addDisableValidationRule("setcd.advanced.tests.powerdns.com.")
+ addAction("setcd.advanced.tests.powerdns.com.", DisableValidationAction())
addAction(makeRule("setcdviaaction.advanced.tests.powerdns.com."), DisableValidationAction())
newServer{address="127.0.0.1:%s"}
"""
class TestAdvancedClearRD(DNSDistTest):
_config_template = """
- addNoRecurseRule("clearrd.advanced.tests.powerdns.com.")
+ addAction("clearrd.advanced.tests.powerdns.com.", NoRecurseAction())
addAction(makeRule("clearrdviaaction.advanced.tests.powerdns.com."), NoRecurseAction())
newServer{address="127.0.0.1:%s"}
"""
_config_template = """
newServer{address="127.0.0.1:%s", pool="real"}
- addPoolRule("pool.routing.tests.powerdns.com", "real")
addAction(makeRule("poolaction.routing.tests.powerdns.com"), PoolAction("real"))
- addQPSPoolRule("qpspool.routing.tests.powerdns.com", 10, "abuse")
- addAction(makeRule("qpspoolaction.routing.tests.powerdns.com"), QPSPoolAction(10, "abuse"))
"""
- def testPolicyPool(self):
- """
- Routing: Set pool by qname
-
- Send an A query to "pool.routing.tests.powerdns.com.",
- check that dnsdist routes the query to the "real" pool.
- """
- name = 'pool.routing.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
- response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
- response.answer.append(rrset)
-
- (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
- receivedQuery.id = query.id
- self.assertEquals(query, receivedQuery)
- self.assertEquals(response, receivedResponse)
-
- (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
- receivedQuery.id = query.id
- self.assertEquals(query, receivedQuery)
- self.assertEquals(response, receivedResponse)
-
def testPolicyPoolAction(self):
"""
Routing: Set pool by qname via PoolAction
Send an A query to "poolaction.routing.tests.powerdns.com.",
check that dnsdist routes the query to the "real" pool.
"""
- name = 'pool.routing.tests.powerdns.com.'
+ name = 'poolaction.routing.tests.powerdns.com.'
query = dns.message.make_query(name, 'A', 'IN')
response = dns.message.make_response(query)
rrset = dns.rrset.from_text(name,
class TestRoutingQPSPoolRouting(DNSDistTest):
_config_template = """
newServer{address="127.0.0.1:%s", pool="regular"}
- addQPSPoolRule("qpspool.routing.tests.powerdns.com", 10, "regular")
addAction(makeRule("qpspoolaction.routing.tests.powerdns.com"), QPSPoolAction(10, "regular"))
"""
- def testQPSPool(self):
- """
- Routing: Set pool by QPS
-
- Send queries to "qpspool.routing.tests.powerdns.com."
- check that dnsdist does not route the query to the "regular" pool
- when the max QPS has been reached.
- """
- maxQPS = 10
- name = 'qpspool.routing.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
- response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
- response.answer.append(rrset)
-
- for _ in range(maxQPS):
- (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
- receivedQuery.id = query.id
- self.assertEquals(query, receivedQuery)
- self.assertEquals(response, receivedResponse)
-
- # we should now be sent to the "abuse" pool which is empty,
- # so the queries should be dropped
- (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
- self.assertEquals(receivedResponse, None)
-
- time.sleep(1)
-
- # again, over TCP this time
- for _ in range(maxQPS):
- (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
- receivedQuery.id = query.id
- self.assertEquals(query, receivedQuery)
- self.assertEquals(response, receivedResponse)
-
-
- (_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False)
- self.assertEquals(receivedResponse, None)
-
def testQPSPoolAction(self):
"""
Routing: Set pool by QPS via action
class TestSpoofingSpoof(DNSDistTest):
_config_template = """
- addDomainSpoof("spoof.spoofing.tests.powerdns.com.", "192.0.2.1", "2001:DB8::1")
- addDomainCNAMESpoof("cnamespoof.spoofing.tests.powerdns.com.", "cname.spoofing.tests.powerdns.com.")
addAction(makeRule("spoofaction.spoofing.tests.powerdns.com."), SpoofAction("192.0.2.1", "2001:DB8::1"))
addAction(makeRule("cnamespoofaction.spoofing.tests.powerdns.com."), SpoofCNAMEAction("cnameaction.spoofing.tests.powerdns.com."))
- addDomainSpoof("multispoof.spoofing.tests.powerdns.com", {"192.0.2.1", "192.0.2.2", "2001:DB8::1", "2001:DB8::2"})
+ addAction("multispoof.spoofing.tests.powerdns.com", SpoofAction({"192.0.2.1", "192.0.2.2", "2001:DB8::1", "2001:DB8::2"}))
newServer{address="127.0.0.1:%s"}
"""
- def testSpoofA(self):
- """
- Spoofing: Spoof A
-
- Send an A query to "spoof.spoofing.tests.powerdns.com.",
- check that dnsdist sends a spoofed result.
- """
- name = 'spoof.spoofing.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
- # dnsdist set RA = RD for spoofed responses
- query.flags &= ~dns.flags.RD
- expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
- expectedResponse.answer.append(rrset)
-
- (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
- self.assertTrue(receivedResponse)
- self.assertEquals(expectedResponse, receivedResponse)
-
- (_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False)
- self.assertTrue(receivedResponse)
- self.assertEquals(expectedResponse, receivedResponse)
-
- def testSpoofAAAA(self):
- """
- Spoofing: Spoof AAAA
-
- Send an AAAA query to "spoof.spoofing.tests.powerdns.com.",
- check that dnsdist sends a spoofed result.
- """
- name = 'spoof.spoofing.tests.powerdns.com.'
- query = dns.message.make_query(name, 'AAAA', 'IN')
- # dnsdist set RA = RD for spoofed responses
- query.flags &= ~dns.flags.RD
- expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.AAAA,
- '2001:DB8::1')
- expectedResponse.answer.append(rrset)
-
- (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
- self.assertTrue(receivedResponse)
- self.assertEquals(expectedResponse, receivedResponse)
-
- (_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False)
- self.assertTrue(receivedResponse)
- self.assertEquals(expectedResponse, receivedResponse)
-
- def testSpoofCNAME(self):
- """
- Spoofing: Spoof CNAME
-
- Send an A query for "cnamespoof.spoofing.tests.powerdns.com.",
- check that dnsdist sends a spoofed result.
- """
- name = 'cnamespoof.spoofing.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
- # dnsdist set RA = RD for spoofed responses
- query.flags &= ~dns.flags.RD
- expectedResponse = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.CNAME,
- 'cname.spoofing.tests.powerdns.com.')
- expectedResponse.answer.append(rrset)
-
- (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
- self.assertTrue(receivedResponse)
- self.assertEquals(expectedResponse, receivedResponse)
-
- (_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False)
- self.assertTrue(receivedResponse)
- self.assertEquals(expectedResponse, receivedResponse)
-
def testSpoofActionA(self):
"""
Spoofing: Spoof A via Action