/* * 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 "bpf-filter.hh" #include "config.h" #include "dnsdist.hh" #include "dnsdist-async.hh" #include "dnsdist-lua.hh" #include "dnsdist-resolver.hh" #include "dnsdist-svc.hh" #include "dnsdist-xsk.hh" #include "dolog.hh" #include "xsk.hh" // NOLINTNEXTLINE(readability-function-cognitive-complexity): this function declares Lua bindings, even with a good refactoring it will likely blow up the threshold void setupLuaBindings(LuaContext& luaCtx, bool client, bool configCheck) { luaCtx.writeFunction("vinfolog", [](const string& arg) { vinfolog("%s", arg); }); luaCtx.writeFunction("infolog", [](const string& arg) { infolog("%s", arg); }); luaCtx.writeFunction("errlog", [](const string& arg) { errlog("%s", arg); }); luaCtx.writeFunction("warnlog", [](const string& arg) { warnlog("%s", arg); }); luaCtx.writeFunction("show", [](const string& arg) { g_outputBuffer+=arg; g_outputBuffer+="\n"; }); /* Exceptions */ luaCtx.registerFunction("__tostring", [](const std::exception_ptr& eptr) -> std::string { try { if (eptr) { std::rethrow_exception(eptr); } } catch(const std::exception& e) { return string(e.what()); } catch(const PDNSException& e) { return e.reason; } catch(...) { return string("Unknown exception"); } return string("No exception"); }); #ifndef DISABLE_POLICIES_BINDINGS /* ServerPolicy */ luaCtx.writeFunction("newServerPolicy", [](string name, ServerPolicy::policyfunc_t policy) { return std::make_shared(name, policy, true);}); luaCtx.registerMember("name", &ServerPolicy::d_name); luaCtx.registerMember("policy", &ServerPolicy::d_policy); luaCtx.registerMember("ffipolicy", &ServerPolicy::d_ffipolicy); luaCtx.registerMember("isLua", &ServerPolicy::d_isLua); luaCtx.registerMember("isFFI", &ServerPolicy::d_isFFI); luaCtx.registerMember("isPerThread", &ServerPolicy::d_isPerThread); luaCtx.registerFunction("toString", &ServerPolicy::toString); luaCtx.registerFunction("__tostring", &ServerPolicy::toString); const std::array, 6> policies = { std::make_shared("firstAvailable", firstAvailable, false), std::make_shared("roundrobin", roundrobin, false), std::make_shared("wrandom", wrandom, false), std::make_shared("whashed", whashed, false), std::make_shared("chashed", chashed, false), std::make_shared("leastOutstanding", leastOutstanding, false) }; for (const auto& policy : policies) { luaCtx.writeVariable(policy->d_name, policy); } #endif /* DISABLE_POLICIES_BINDINGS */ /* ServerPool */ luaCtx.registerFunction::*)(std::shared_ptr)>("setCache", [](std::shared_ptr pool, std::shared_ptr cache) { if (pool) { pool->packetCache = std::move(cache); } }); luaCtx.registerFunction("getCache", &ServerPool::getCache); luaCtx.registerFunction::*)()>("unsetCache", [](std::shared_ptr pool) { if (pool) { pool->packetCache = nullptr; } }); luaCtx.registerFunction("getECS", &ServerPool::getECS); luaCtx.registerFunction("setECS", &ServerPool::setECS); #ifndef DISABLE_DOWNSTREAM_BINDINGS /* DownstreamState */ luaCtx.registerFunction("setQPS", [](DownstreamState& state, int lim) { state.qps = lim > 0 ? QPSLimiter(lim, lim) : QPSLimiter(); }); luaCtx.registerFunction::*)(string)>("addPool", [](const std::shared_ptr& state, const string& pool) { auto localPools = g_pools.getCopy(); addServerToPool(localPools, pool, state); g_pools.setState(localPools); state->d_config.pools.insert(pool); }); luaCtx.registerFunction::*)(string)>("rmPool", [](const std::shared_ptr& state, const string& pool) { auto localPools = g_pools.getCopy(); removeServerFromPool(localPools, pool, state); g_pools.setState(localPools); state->d_config.pools.erase(pool); }); luaCtx.registerFunction("getOutstanding", [](const DownstreamState& state) { return state.outstanding.load(); }); luaCtx.registerFunction("getDrops", [](const DownstreamState& state) { return state.reuseds.load(); }); luaCtx.registerFunction("getLatency", [](const DownstreamState& state) { return state.getRelevantLatencyUsec(); }); luaCtx.registerFunction("isUp", &DownstreamState::isUp); luaCtx.registerFunction("setDown", &DownstreamState::setDown); luaCtx.registerFunction("setUp", &DownstreamState::setUp); luaCtx.registerFunction newStatus)>("setAuto", [](DownstreamState& state, boost::optional newStatus) { if (newStatus) { state.setUpStatus(*newStatus); } state.setAuto(); }); luaCtx.registerFunction newStatus)>("setLazyAuto", [](DownstreamState& state, boost::optional newStatus) { if (newStatus) { state.setUpStatus(*newStatus); } state.setLazyAuto(); }); luaCtx.registerFunction("getName", [](const DownstreamState& state) -> const std::string& { return state.getName(); }); luaCtx.registerFunction("getNameWithAddr", [](const DownstreamState& state) -> const std::string& { return state.getNameWithAddr(); }); luaCtx.registerMember("upStatus", &DownstreamState::upStatus); luaCtx.registerMember("weight", [](const DownstreamState& state) -> int {return state.d_config.d_weight;}, [](DownstreamState& state, int newWeight) { state.setWeight(newWeight); } ); luaCtx.registerMember("order", [](const DownstreamState& state) -> int {return state.d_config.order; }, [](DownstreamState& state, int newOrder) { state.d_config.order = newOrder; } ); luaCtx.registerMember("name", [](const DownstreamState& backend) -> const std::string { return backend.getName(); }, [](DownstreamState& backend, const std::string& newName) { backend.setName(newName); }); luaCtx.registerFunction("getID", [](const DownstreamState& state) { return boost::uuids::to_string(*state.d_config.id); }); #endif /* DISABLE_DOWNSTREAM_BINDINGS */ #ifndef DISABLE_DNSHEADER_BINDINGS /* dnsheader */ luaCtx.registerFunction("setRD", [](dnsheader& dh, bool v) { dh.rd=v; }); luaCtx.registerFunction("getRD", [](const dnsheader& dh) { return (bool)dh.rd; }); luaCtx.registerFunction("setRA", [](dnsheader& dh, bool v) { dh.ra=v; }); luaCtx.registerFunction("getRA", [](const dnsheader& dh) { return (bool)dh.ra; }); luaCtx.registerFunction("setAD", [](dnsheader& dh, bool v) { dh.ad=v; }); luaCtx.registerFunction("getAD", [](const dnsheader& dh) { return (bool)dh.ad; }); luaCtx.registerFunction("setAA", [](dnsheader& dh, bool v) { dh.aa=v; }); luaCtx.registerFunction("getAA", [](const dnsheader& dh) { return (bool)dh.aa; }); luaCtx.registerFunction("setCD", [](dnsheader& dh, bool v) { dh.cd=v; }); luaCtx.registerFunction("getCD", [](const dnsheader& dh) { return (bool)dh.cd; }); luaCtx.registerFunction("getID", [](const dnsheader& dh) { return ntohs(dh.id); }); luaCtx.registerFunction("getTC", [](const dnsheader& dh) { return (bool)dh.tc; }); luaCtx.registerFunction("setTC", [](dnsheader& dh, bool v) { dh.tc=v; if(v) dh.ra = dh.rd; // you'll always need this, otherwise TC=1 gets ignored }); luaCtx.registerFunction("setQR", [](dnsheader& dh, bool v) { dh.qr=v; }); #endif /* DISABLE_DNSHEADER_BINDINGS */ #ifndef DISABLE_COMBO_ADDR_BINDINGS /* ComboAddress */ luaCtx.writeFunction("newCA", [](const std::string& name) { return ComboAddress(name); }); luaCtx.writeFunction("newCAFromRaw", [](const std::string& raw, boost::optional port) { if (raw.size() == 4) { struct sockaddr_in sin4; memset(&sin4, 0, sizeof(sin4)); sin4.sin_family = AF_INET; memcpy(&sin4.sin_addr.s_addr, raw.c_str(), raw.size()); if (port) { sin4.sin_port = htons(*port); } return ComboAddress(&sin4); } else if (raw.size() == 16) { struct sockaddr_in6 sin6; memset(&sin6, 0, sizeof(sin6)); sin6.sin6_family = AF_INET6; memcpy(&sin6.sin6_addr.s6_addr, raw.c_str(), raw.size()); if (port) { sin6.sin6_port = htons(*port); } return ComboAddress(&sin6); } return ComboAddress(); }); luaCtx.registerFunction("tostring", [](const ComboAddress& ca) { return ca.toString(); }); luaCtx.registerFunction("tostringWithPort", [](const ComboAddress& ca) { return ca.toStringWithPort(); }); luaCtx.registerFunction("__tostring", [](const ComboAddress& ca) { return ca.toString(); }); luaCtx.registerFunction("toString", [](const ComboAddress& ca) { return ca.toString(); }); luaCtx.registerFunction("toStringWithPort", [](const ComboAddress& ca) { return ca.toStringWithPort(); }); luaCtx.registerFunction("getPort", [](const ComboAddress& ca) { return ntohs(ca.sin4.sin_port); } ); luaCtx.registerFunction("truncate", [](ComboAddress& ca, unsigned int bits) { ca.truncate(bits); }); luaCtx.registerFunction("isIPv4", [](const ComboAddress& ca) { return ca.sin4.sin_family == AF_INET; }); luaCtx.registerFunction("isIPv6", [](const ComboAddress& ca) { return ca.sin4.sin_family == AF_INET6; }); luaCtx.registerFunction("isMappedIPv4", [](const ComboAddress& ca) { return ca.isMappedIPv4(); }); luaCtx.registerFunction("mapToIPv4", [](const ComboAddress& ca) { return ca.mapToIPv4(); }); luaCtx.registerFunction("match", [](nmts_t& s, const ComboAddress& ca) { return s.match(ca); }); #endif /* DISABLE_COMBO_ADDR_BINDINGS */ #ifndef DISABLE_DNSNAME_BINDINGS /* DNSName */ luaCtx.registerFunction("isPartOf", &DNSName::isPartOf); luaCtx.registerFunction("chopOff", [](DNSName&dn ) { return dn.chopOff(); }); luaCtx.registerFunction("countLabels", [](const DNSName& name) { return name.countLabels(); }); luaCtx.registerFunction("hash", [](const DNSName& name) { return name.hash(); }); luaCtx.registerFunction("wirelength", [](const DNSName& name) { return name.wirelength(); }); luaCtx.registerFunction("tostring", [](const DNSName&dn ) { return dn.toString(); }); luaCtx.registerFunction("toString", [](const DNSName&dn ) { return dn.toString(); }); luaCtx.registerFunction("toStringNoDot", [](const DNSName&dn ) { return dn.toStringNoDot(); }); luaCtx.registerFunction("__tostring", [](const DNSName&dn ) { return dn.toString(); }); luaCtx.registerFunction("toDNSString", [](const DNSName&dn ) { return dn.toDNSString(); }); luaCtx.registerFunction("makeRelative", [](const DNSName& dn, const DNSName& to) { return dn.makeRelative(to); }); luaCtx.writeFunction("newDNSName", [](const std::string& name) { return DNSName(name); }); luaCtx.writeFunction("newDNSNameFromRaw", [](const std::string& name) { return DNSName(name.c_str(), name.size(), 0, false); }); luaCtx.writeFunction("newSuffixMatchNode", []() { return SuffixMatchNode(); }); luaCtx.writeFunction("newDNSNameSet", []() { return DNSNameSet(); }); /* DNSNameSet */ luaCtx.registerFunction("toString", [](const DNSNameSet&dns ) { return dns.toString(); }); luaCtx.registerFunction("__tostring", [](const DNSNameSet&dns ) { return dns.toString(); }); luaCtx.registerFunction("add", [](DNSNameSet& dns, DNSName& dn) { dns.insert(dn); }); luaCtx.registerFunction("check", [](DNSNameSet& dns, DNSName& dn) { return dns.find(dn) != dns.end(); }); luaCtx.registerFunction("delete",(size_t (DNSNameSet::*)(const DNSName&)) &DNSNameSet::erase); luaCtx.registerFunction("size",(size_t (DNSNameSet::*)() const) &DNSNameSet::size); luaCtx.registerFunction("clear",(void (DNSNameSet::*)()) &DNSNameSet::clear); luaCtx.registerFunction("empty",(bool (DNSNameSet::*)() const) &DNSNameSet::empty); #endif /* DISABLE_DNSNAME_BINDINGS */ #ifndef DISABLE_SUFFIX_MATCH_BINDINGS /* SuffixMatchNode */ luaCtx.registerFunction, LuaArray> &name)>("add", [](SuffixMatchNode &smn, const boost::variant, LuaArray> &name) { if (name.type() == typeid(DNSName)) { const auto& actualName = boost::get(name); smn.add(actualName); return; } if (name.type() == typeid(std::string)) { const auto& actualName = boost::get(name); smn.add(actualName); return; } if (name.type() == typeid(LuaArray)) { const auto& names = boost::get>(name); for (const auto& actualName : names) { smn.add(actualName.second); } return; } if (name.type() == typeid(LuaArray)) { const auto& names = boost::get>(name); for (const auto& actualName : names) { smn.add(actualName.second); } return; } }); luaCtx.registerFunction, LuaArray> &name)>("remove", [](SuffixMatchNode &smn, const boost::variant, LuaArray> &name) { if (name.type() == typeid(DNSName)) { const auto& actualName = boost::get(name); smn.remove(actualName); return; } if (name.type() == typeid(string)) { const auto& actualName = boost::get(name); DNSName dnsName(actualName); smn.remove(dnsName); return; } if (name.type() == typeid(LuaArray)) { const auto& names = boost::get>(name); for (const auto& actualName : names) { smn.remove(actualName.second); } return; } if (name.type() == typeid(LuaArray)) { const auto& names = boost::get>(name); for (const auto& actualName : names) { DNSName dnsName(actualName.second); smn.remove(dnsName); } return; } }); luaCtx.registerFunction("check", (bool (SuffixMatchNode::*)(const DNSName&) const) &SuffixMatchNode::check); luaCtx.registerFunction (SuffixMatchNode::*)(const DNSName&) const>("getBestMatch", [](const SuffixMatchNode& smn, const DNSName& needle) { boost::optional result{boost::none}; auto res = smn.getBestMatch(needle); if (res) { result = *res; } return result; }); #endif /* DISABLE_SUFFIX_MATCH_BINDINGS */ #ifndef DISABLE_NETMASK_BINDINGS /* Netmask */ luaCtx.writeFunction("newNetmask", [](boost::variant addrOrStr, boost::optional bits) { if (addrOrStr.type() == typeid(ComboAddress)) { const auto& comboAddr = boost::get(addrOrStr); if (bits) { return Netmask(comboAddr, *bits); } return Netmask(comboAddr); } if (addrOrStr.type() == typeid(std::string)) { const auto& str = boost::get(addrOrStr); return Netmask(str); } throw std::runtime_error("Invalid parameter passed to 'newNetmask()'"); }); luaCtx.registerFunction("empty", &Netmask::empty); luaCtx.registerFunction("getBits", &Netmask::getBits); luaCtx.registerFunction("getNetwork", [](const Netmask& nm) { return nm.getNetwork(); } ); // const reference makes this necessary luaCtx.registerFunction("getMaskedNetwork", [](const Netmask& nm) { return nm.getMaskedNetwork(); } ); luaCtx.registerFunction("isIpv4", &Netmask::isIPv4); luaCtx.registerFunction("isIPv4", &Netmask::isIPv4); luaCtx.registerFunction("isIpv6", &Netmask::isIPv6); luaCtx.registerFunction("isIPv6", &Netmask::isIPv6); luaCtx.registerFunction("match", (bool (Netmask::*)(const string&) const)&Netmask::match); luaCtx.registerFunction("toString", &Netmask::toString); luaCtx.registerFunction("__tostring", &Netmask::toString); luaCtx.registerEqFunction(&Netmask::operator==); luaCtx.registerToStringFunction(&Netmask::toString); /* NetmaskGroup */ luaCtx.writeFunction("newNMG", []() { return NetmaskGroup(); }); luaCtx.registerFunction("addMask", [](NetmaskGroup& nmg, const std::string& mask) { nmg.addMask(mask); }); luaCtx.registerFunction("addNMG", [](NetmaskGroup& nmg, const NetmaskGroup& otherNMG) { /* this is not going to be very efficient, sorry */ auto entries = otherNMG.toStringVector(); for (const auto& entry : entries) { nmg.addMask(entry); } }); luaCtx.registerFunction& map)>("addMasks", [](NetmaskGroup&nmg, const std::map& map) { for (const auto& entry : map) { nmg.addMask(Netmask(entry.first)); } }); luaCtx.registerFunction("match", (bool (NetmaskGroup::*)(const ComboAddress&) const)&NetmaskGroup::match); luaCtx.registerFunction("size", &NetmaskGroup::size); luaCtx.registerFunction("clear", &NetmaskGroup::clear); luaCtx.registerFunction("toString", [](const NetmaskGroup& nmg ) { return "NetmaskGroup " + nmg.toString(); }); luaCtx.registerFunction("__tostring", [](const NetmaskGroup& nmg ) { return "NetmaskGroup " + nmg.toString(); }); #endif /* DISABLE_NETMASK_BINDINGS */ #ifndef DISABLE_QPS_LIMITER_BINDINGS /* QPSLimiter */ luaCtx.writeFunction("newQPSLimiter", [](int rate, int burst) { return QPSLimiter(rate, burst); }); luaCtx.registerFunction("check", &QPSLimiter::check); #endif /* DISABLE_QPS_LIMITER_BINDINGS */ #ifndef DISABLE_CLIENT_STATE_BINDINGS /* ClientState */ luaCtx.registerFunction("toString", [](const ClientState& fe) { setLuaNoSideEffect(); return fe.local.toStringWithPort(); }); luaCtx.registerFunction("__tostring", [](const ClientState& fe) { setLuaNoSideEffect(); return fe.local.toStringWithPort(); }); luaCtx.registerFunction("getType", [](const ClientState& fe) { setLuaNoSideEffect(); return fe.getType(); }); luaCtx.registerFunction("getConfiguredTLSProvider", [](const ClientState& fe) { setLuaNoSideEffect(); if (fe.tlsFrontend != nullptr) { return fe.tlsFrontend->getRequestedProvider(); } else if (fe.dohFrontend != nullptr) { return std::string("openssl"); } return std::string(); }); luaCtx.registerFunction("getEffectiveTLSProvider", [](const ClientState& fe) { setLuaNoSideEffect(); if (fe.tlsFrontend != nullptr) { return fe.tlsFrontend->getEffectiveProvider(); } else if (fe.dohFrontend != nullptr) { return std::string("openssl"); } return std::string(); }); luaCtx.registerMember("muted", &ClientState::muted); #ifdef HAVE_EBPF luaCtx.registerFunction)>("attachFilter", [](ClientState& frontend, std::shared_ptr bpf) { if (bpf) { frontend.attachFilter(bpf, frontend.getSocket()); } }); luaCtx.registerFunction("detachFilter", [](ClientState& frontend) { frontend.detachFilter(frontend.getSocket()); }); #endif /* HAVE_EBPF */ #endif /* DISABLE_CLIENT_STATE_BINDINGS */ /* BPF Filter */ #ifdef HAVE_EBPF using bpfopts_t = LuaAssociativeTable>; luaCtx.writeFunction("newBPFFilter", [client](bpfopts_t opts) { if (client) { return std::shared_ptr(nullptr); } std::unordered_map mapsConfig; const auto convertParamsToConfig = [&](const std::string& name, BPFFilter::MapType type) { BPFFilter::MapConfiguration config; config.d_type = type; if (const string key = name + "MaxItems"; opts.count(key)) { const auto& tmp = opts.at(key); if (tmp.type() != typeid(uint32_t)) { throw std::runtime_error("params is invalid"); } const auto& params = boost::get(tmp); config.d_maxItems = params; } if (const string key = name + "PinnedPath"; opts.count(key)) { auto& tmp = opts.at(key); if (tmp.type() != typeid(string)) { throw std::runtime_error("params is invalid"); } auto& params = boost::get(tmp); config.d_pinnedPath = std::move(params); } mapsConfig[name] = std::move(config); }; convertParamsToConfig("ipv4", BPFFilter::MapType::IPv4); convertParamsToConfig("ipv6", BPFFilter::MapType::IPv6); convertParamsToConfig("qnames", BPFFilter::MapType::QNames); convertParamsToConfig("cidr4", BPFFilter::MapType::CIDR4); convertParamsToConfig("cidr6", BPFFilter::MapType::CIDR6); BPFFilter::MapFormat format = BPFFilter::MapFormat::Legacy; bool external = false; if (opts.count("external")) { const auto& tmp = opts.at("external"); if (tmp.type() != typeid(bool)) { throw std::runtime_error("params is invalid"); } if ((external = boost::get(tmp))) { format = BPFFilter::MapFormat::WithActions; } } return std::make_shared(mapsConfig, format, external); }); luaCtx.registerFunction::*)(const ComboAddress& ca, boost::optional action)>("block", [](std::shared_ptr bpf, const ComboAddress& ca, boost::optional action) { if (bpf) { if (!action) { return bpf->block(ca, BPFFilter::MatchAction::Drop); } else { BPFFilter::MatchAction match; switch (*action) { case 0: match = BPFFilter::MatchAction::Pass; break; case 1: match = BPFFilter::MatchAction::Drop; break; case 2: match = BPFFilter::MatchAction::Truncate; break; default: throw std::runtime_error("Unsupported action for BPFFilter::block"); } return bpf->block(ca, match); } } }); luaCtx.registerFunction::*)(const string& range, uint32_t action, boost::optional force)>("addRangeRule", [](std::shared_ptr bpf, const string& range, uint32_t action, boost::optional force) { if (!bpf) { return; } BPFFilter::MatchAction match; switch (action) { case 0: match = BPFFilter::MatchAction::Pass; break; case 1: match = BPFFilter::MatchAction::Drop; break; case 2: match = BPFFilter::MatchAction::Truncate; break; default: throw std::runtime_error("Unsupported action for BPFFilter::block"); } return bpf->addRangeRule(Netmask(range), force ? *force : false, match); }); luaCtx.registerFunction::*)(const DNSName& qname, boost::optional qtype, boost::optional action)>("blockQName", [](std::shared_ptr bpf, const DNSName& qname, boost::optional qtype, boost::optional action) { if (bpf) { if (!action) { return bpf->block(qname, BPFFilter::MatchAction::Drop, qtype ? *qtype : 255); } else { BPFFilter::MatchAction match; switch (*action) { case 0: match = BPFFilter::MatchAction::Pass; break; case 1: match = BPFFilter::MatchAction::Drop; break; case 2: match = BPFFilter::MatchAction::Truncate; break; default: throw std::runtime_error("Unsupported action for BPFFilter::blockQName"); } return bpf->block(qname, match, qtype ? *qtype : 255); } } }); luaCtx.registerFunction::*)(const ComboAddress& ca)>("unblock", [](std::shared_ptr bpf, const ComboAddress& ca) { if (bpf) { return bpf->unblock(ca); } }); luaCtx.registerFunction::*)(const string& range)>("rmRangeRule", [](std::shared_ptr bpf, const string& range) { if (!bpf) { return; } bpf->rmRangeRule(Netmask(range)); }); luaCtx.registerFunction::*)() const>("lsRangeRule", [](const std::shared_ptr bpf) { setLuaNoSideEffect(); std::string res; if (!bpf) { return res; } const auto rangeStat = bpf->getRangeRule(); for (const auto& value : rangeStat) { if (value.first.isIPv4()) { res += BPFFilter::toString(value.second.action) + "\t " + value.first.toString() + "\n"; } else if (value.first.isIPv6()) { res += BPFFilter::toString(value.second.action) + "\t[" + value.first.toString() + "]\n"; } } return res; }); luaCtx.registerFunction::*)(const DNSName& qname, boost::optional qtype)>("unblockQName", [](std::shared_ptr bpf, const DNSName& qname, boost::optional qtype) { if (bpf) { return bpf->unblock(qname, qtype ? *qtype : 255); } }); luaCtx.registerFunction::*)()const>("getStats", [](const std::shared_ptr bpf) { setLuaNoSideEffect(); std::string res; if (bpf) { auto 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"; } } const auto rangeStat = bpf->getRangeRule(); for (const auto& value : rangeStat) { if (value.first.isIPv4()) { res += BPFFilter::toString(value.second.action) + "\t " + value.first.toString() + ": " + std::to_string(value.second.counter) + "\n"; } else if (value.first.isIPv6()) { res += BPFFilter::toString(value.second.action) + "\t[" + value.first.toString() + "]: " + std::to_string(value.second.counter) + "\n"; } } auto 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; }); luaCtx.registerFunction::*)()>("attachToAllBinds", [](std::shared_ptr bpf) { std::string res; if (!g_configurationDone) { throw std::runtime_error("attachToAllBinds() cannot be used at configuration time!"); return; } if (bpf) { for (const auto& frontend : g_frontends) { frontend->attachFilter(bpf, frontend->getSocket()); } } }); luaCtx.writeFunction("newDynBPFFilter", [client](std::shared_ptr bpf) { if (client) { return std::shared_ptr(nullptr); } return std::make_shared(bpf); }); luaCtx.registerFunction::*)(const ComboAddress& addr, boost::optional seconds)>("block", [](std::shared_ptr dbpf, const ComboAddress& addr, boost::optional seconds) { if (dbpf) { struct timespec until; clock_gettime(CLOCK_MONOTONIC, &until); until.tv_sec += seconds ? *seconds : 10; dbpf->block(addr, until); } }); luaCtx.registerFunction::*)()>("purgeExpired", [](std::shared_ptr dbpf) { if (dbpf) { struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); dbpf->purgeExpired(now); } }); luaCtx.registerFunction::*)(LuaTypeOrArrayOf)>("excludeRange", [](std::shared_ptr dbpf, LuaTypeOrArrayOf ranges) { if (!dbpf) { return; } if (ranges.type() == typeid(LuaArray)) { for (const auto& range : *boost::get>(&ranges)) { dbpf->excludeRange(Netmask(range.second)); } } else { dbpf->excludeRange(Netmask(*boost::get(&ranges))); } }); luaCtx.registerFunction::*)(LuaTypeOrArrayOf)>("includeRange", [](std::shared_ptr dbpf, LuaTypeOrArrayOf ranges) { if (!dbpf) { return; } if (ranges.type() == typeid(LuaArray)) { for (const auto& range : *boost::get>(&ranges)) { dbpf->includeRange(Netmask(range.second)); } } else { dbpf->includeRange(Netmask(*boost::get(&ranges))); } }); #endif /* HAVE_EBPF */ #ifdef HAVE_XSK using xskopt_t = LuaAssociativeTable>; luaCtx.writeFunction("newXsk", [client](xskopt_t opts) { if (g_configurationDone) { throw std::runtime_error("newXsk() only can be used at configuration time!"); } if (client) { return std::shared_ptr(nullptr); } uint32_t queue_id; uint32_t frameNums{65536}; std::string ifName; std::string path("/sys/fs/bpf/dnsdist/xskmap"); if (opts.count("ifName") == 1) { ifName = boost::get(opts.at("ifName")); } else { throw std::runtime_error("ifName field is required!"); } if (opts.count("NIC_queue_id") == 1) { queue_id = boost::get(opts.at("NIC_queue_id")); } else { throw std::runtime_error("NIC_queue_id field is required!"); } if (opts.count("frameNums") == 1) { frameNums = boost::get(opts.at("frameNums")); } if (opts.count("xskMapPath") == 1) { path = boost::get(opts.at("xskMapPath")); } auto socket = std::make_shared(frameNums, ifName, queue_id, path); dnsdist::xsk::g_xsk.push_back(socket); return socket; }); luaCtx.registerFunction::*)()const>("getMetrics", [](const std::shared_ptr& xsk) -> std::string { if (!xsk) { return {}; } return xsk->getMetrics(); }); #endif /* HAVE_XSK */ /* EDNSOptionView */ luaCtx.registerFunction("count", [](const EDNSOptionView& option) { return option.values.size(); }); luaCtx.registerFunction(EDNSOptionView::*)()const>("getValues", [] (const EDNSOptionView& option) { std::vector values; for (const auto& value : option.values) { values.push_back(std::string(value.content, value.size)); } return values; }); luaCtx.writeFunction("newDOHResponseMapEntry", [](const std::string& regex, uint64_t status, const std::string& content, boost::optional> customHeaders) { checkParameterBound("newDOHResponseMapEntry", status, std::numeric_limits::max()); boost::optional> headers{boost::none}; if (customHeaders) { headers = LuaAssociativeTable(); for (const auto& header : *customHeaders) { (*headers)[boost::to_lower_copy(header.first)] = header.second; } } return std::make_shared(regex, status, PacketBuffer(content.begin(), content.end()), headers); }); luaCtx.writeFunction("newSVCRecordParameters", [](uint64_t priority, const std::string& target, boost::optional additionalParameters) { checkParameterBound("newSVCRecordParameters", priority, std::numeric_limits::max()); SVCRecordParameters parameters; if (additionalParameters) { parameters = parseSVCParameters(*additionalParameters); } parameters.priority = priority; parameters.target = DNSName(target); return parameters; }); luaCtx.writeFunction("getListOfNetworkInterfaces", []() { LuaArray result; auto itfs = getListOfNetworkInterfaces(); int counter = 1; for (const auto& itf : itfs) { result.push_back({counter++, itf}); } return result; }); luaCtx.writeFunction("getListOfAddressesOfNetworkInterface", [](const std::string& itf) { LuaArray result; auto addrs = getListOfAddressesOfNetworkInterface(itf); int counter = 1; for (const auto& addr : addrs) { result.push_back({counter++, addr.toString()}); } return result; }); luaCtx.writeFunction("getListOfRangesOfNetworkInterface", [](const std::string& itf) { LuaArray result; auto addrs = getListOfRangesOfNetworkInterface(itf); int counter = 1; for (const auto& addr : addrs) { result.push_back({counter++, addr.toString()}); } return result; }); luaCtx.writeFunction("getMACAddress", [](const std::string& ip) { return getMACAddress(ComboAddress(ip)); }); luaCtx.writeFunction("getCurrentTime", []() -> timespec { timespec now; if (gettime(&now, true) < 0) { unixDie("Getting timestamp"); } return now; }); luaCtx.writeFunction("getAddressInfo", [client, configCheck](std::string hostname, std::function& ips)> callback) { if (client || configCheck) { return; } std::thread newThread(dnsdist::resolver::asynchronousResolver, std::move(hostname), [callback=std::move(callback)](const std::string& resolvedHostname, std::vector& ips) { LuaArray result; result.reserve(ips.size()); for (const auto& entry : ips) { result.emplace_back(result.size() + 1, entry); } { auto lua = g_lua.lock(); callback(resolvedHostname, result); dnsdist::handleQueuedAsynchronousEvents(); } }); newThread.detach(); }); }