/* * 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-async.hh" #include "dnsdist-dnsparser.hh" #include "dnsdist-ecs.hh" #include "dnsdist-internal-queries.hh" #include "dnsdist-lua.hh" #include "dnsparser.hh" // NOLINTNEXTLINE(readability-function-cognitive-complexity): this function declares Lua bindings, even with a good refactoring it will likely blow up the threshold void setupLuaBindingsDNSQuestion(LuaContext& luaCtx) { #ifndef DISABLE_NON_FFI_DQ_BINDINGS /* DNSQuestion */ /* PowerDNS DNSQuestion compat */ luaCtx.registerMember("localaddr", [](const DNSQuestion& dq) -> const ComboAddress { return dq.ids.origDest; }, [](DNSQuestion& dq, const ComboAddress newLocal) { (void) newLocal; }); luaCtx.registerMember("qname", [](const DNSQuestion& dq) -> const DNSName { return dq.ids.qname; }, [](DNSQuestion& dq, const DNSName& newName) { (void) newName; }); luaCtx.registerMember("qtype", [](const DNSQuestion& dq) -> uint16_t { return dq.ids.qtype; }, [](DNSQuestion& dq, uint16_t newType) { (void) newType; }); luaCtx.registerMember("qclass", [](const DNSQuestion& dq) -> uint16_t { return dq.ids.qclass; }, [](DNSQuestion& dq, uint16_t newClass) { (void) newClass; }); luaCtx.registerMember("rcode", [](const DNSQuestion& dq) -> int { return static_cast(dq.getHeader()->rcode); }, [](DNSQuestion& dq, int newRCode) { dnsdist::PacketMangling::editDNSHeaderFromPacket(dq.getMutableData(), [newRCode](dnsheader& header) { header.rcode = static_cast(newRCode); return true; }); }); luaCtx.registerMember("remoteaddr", [](const DNSQuestion& dq) -> const ComboAddress { return dq.ids.origRemote; }, [](DNSQuestion& dq, const ComboAddress newRemote) { (void) newRemote; }); /* DNSDist DNSQuestion */ luaCtx.registerMember("dh", [](const DNSQuestion& dq) -> dnsheader* { return dq.getMutableHeader(); }, [](DNSQuestion& dq, const dnsheader* dh) { dnsdist::PacketMangling::editDNSHeaderFromPacket(dq.getMutableData(), [&dh](dnsheader& header) { header = *dh; return true; }); }); luaCtx.registerMember("len", [](const DNSQuestion& dq) -> uint16_t { return dq.getData().size(); }, [](DNSQuestion& dq, uint16_t newlen) { dq.getMutableData().resize(newlen); }); luaCtx.registerMember("opcode", [](const DNSQuestion& dq) -> uint8_t { return dq.getHeader()->opcode; }, [](DNSQuestion& dq, uint8_t newOpcode) { (void) newOpcode; }); luaCtx.registerMember("tcp", [](const DNSQuestion& dq) -> bool { return dq.overTCP(); }, [](DNSQuestion& dq, bool newTcp) { (void) newTcp; }); luaCtx.registerMember("skipCache", [](const DNSQuestion& dq) -> bool { return dq.ids.skipCache; }, [](DNSQuestion& dq, bool newSkipCache) { dq.ids.skipCache = newSkipCache; }); luaCtx.registerMember("pool", [](const DNSQuestion& dq) -> std::string { return dq.ids.poolName; }, [](DNSQuestion& dq, const std::string& newPoolName) { dq.ids.poolName = newPoolName; }); luaCtx.registerMember("useECS", [](const DNSQuestion& dq) -> bool { return dq.useECS; }, [](DNSQuestion& dq, bool useECS) { dq.useECS = useECS; }); luaCtx.registerMember("ecsOverride", [](const DNSQuestion& dq) -> bool { return dq.ecsOverride; }, [](DNSQuestion& dq, bool ecsOverride) { dq.ecsOverride = ecsOverride; }); luaCtx.registerMember("ecsPrefixLength", [](const DNSQuestion& dq) -> uint16_t { return dq.ecsPrefixLength; }, [](DNSQuestion& dq, uint16_t newPrefixLength) { dq.ecsPrefixLength = newPrefixLength; }); luaCtx.registerMember (DNSQuestion::*)>("tempFailureTTL", [](const DNSQuestion& dq) -> boost::optional { return dq.ids.tempFailureTTL; }, [](DNSQuestion& dq, boost::optional newValue) { dq.ids.tempFailureTTL = newValue; } ); luaCtx.registerMember("deviceID", [](const DNSQuestion& dq) -> std::string { if (dq.ids.d_protoBufData) { return dq.ids.d_protoBufData->d_deviceID; } return std::string(); }, [](DNSQuestion& dq, const std::string& newValue) { if (!dq.ids.d_protoBufData) { dq.ids.d_protoBufData = std::make_unique(); } dq.ids.d_protoBufData->d_deviceID = newValue; }); luaCtx.registerMember("deviceName", [](const DNSQuestion& dq) -> std::string { if (dq.ids.d_protoBufData) { return dq.ids.d_protoBufData->d_deviceName; } return std::string(); }, [](DNSQuestion& dq, const std::string& newValue) { if (!dq.ids.d_protoBufData) { dq.ids.d_protoBufData = std::make_unique(); } dq.ids.d_protoBufData->d_deviceName = newValue; }); luaCtx.registerMember("requestorID", [](const DNSQuestion& dq) -> std::string { if (dq.ids.d_protoBufData) { return dq.ids.d_protoBufData->d_requestorID; } return std::string(); }, [](DNSQuestion& dq, const std::string& newValue) { if (!dq.ids.d_protoBufData) { dq.ids.d_protoBufData = std::make_unique(); } dq.ids.d_protoBufData->d_requestorID = newValue; }); luaCtx.registerFunction("getDO", [](const DNSQuestion& dq) { return getEDNSZ(dq) & EDNS_HEADER_FLAG_DO; }); luaCtx.registerFunction("getContent", [](const DNSQuestion& dq) { return std::string(reinterpret_cast(dq.getData().data()), dq.getData().size()); }); luaCtx.registerFunction("setContent", [](DNSQuestion& dq, const std::string& raw) { uint16_t oldID = dq.getHeader()->id; auto& buffer = dq.getMutableData(); buffer.clear(); buffer.insert(buffer.begin(), raw.begin(), raw.end()); dnsdist::PacketMangling::editDNSHeaderFromPacket(buffer, [oldID](dnsheader& header) { header.id = oldID; return true; }); }); luaCtx.registerFunction(DNSQuestion::*)()const>("getEDNSOptions", [](const DNSQuestion& dq) { if (dq.ednsOptions == nullptr) { parseEDNSOptions(dq); if (dq.ednsOptions == nullptr) { throw std::runtime_error("parseEDNSOptions should have populated the EDNS options"); } } return *dq.ednsOptions; }); luaCtx.registerFunction("getTrailingData", [](const DNSQuestion& dq) { return dq.getTrailingData(); }); luaCtx.registerFunction("setTrailingData", [](DNSQuestion& dq, const std::string& tail) { return dq.setTrailingData(tail); }); luaCtx.registerFunction("getServerNameIndication", [](const DNSQuestion& dq) { return dq.sni; }); luaCtx.registerFunction("getProtocol", [](const DNSQuestion& dq) { return dq.getProtocol().toPrettyString(); }); luaCtx.registerFunction("getQueryTime", [](const DNSQuestion& dq) { return dq.ids.queryRealTime.getStartTime(); }); luaCtx.registerFunction("sendTrap", [](const DNSQuestion& dq, boost::optional reason) { #ifdef HAVE_NET_SNMP if (g_snmpAgent && g_snmpTrapsEnabled) { g_snmpAgent->sendDNSTrap(dq, reason ? *reason : ""); } #endif /* HAVE_NET_SNMP */ }); luaCtx.registerFunction("setTag", [](DNSQuestion& dq, const std::string& strLabel, const std::string& strValue) { dq.setTag(strLabel, strValue); }); luaCtx.registerFunction)>("setTagArray", [](DNSQuestion& dq, const LuaAssociativeTable&tags) { for (const auto& tag : tags) { dq.setTag(tag.first, tag.second); } }); luaCtx.registerFunction("getTag", [](const DNSQuestion& dq, const std::string& strLabel) { if (!dq.ids.qTag) { return string(); } std::string strValue; const auto it = dq.ids.qTag->find(strLabel); if (it == dq.ids.qTag->cend()) { return string(); } return it->second; }); luaCtx.registerFunction("getTagArray", [](const DNSQuestion& dq) { if (!dq.ids.qTag) { QTag empty; return empty; } return *dq.ids.qTag; }); luaCtx.registerFunction)>("setProxyProtocolValues", [](DNSQuestion& dq, const LuaArray& values) { if (!dq.proxyProtocolValues) { dq.proxyProtocolValues = make_unique>(); } dq.proxyProtocolValues->clear(); dq.proxyProtocolValues->reserve(values.size()); for (const auto& value : values) { checkParameterBound("setProxyProtocolValues", value.first, std::numeric_limits::max()); dq.proxyProtocolValues->push_back({value.second, static_cast(value.first)}); } }); luaCtx.registerFunction("addProxyProtocolValue", [](DNSQuestion& dq, uint64_t type, std::string value) { checkParameterBound("addProxyProtocolValue", type, std::numeric_limits::max()); if (!dq.proxyProtocolValues) { dq.proxyProtocolValues = make_unique>(); } dq.proxyProtocolValues->push_back({std::move(value), static_cast(type)}); }); luaCtx.registerFunction(DNSQuestion::*)()>("getProxyProtocolValues", [](const DNSQuestion& dq) { LuaArray result; if (!dq.proxyProtocolValues) { return result; } result.resize(dq.proxyProtocolValues->size()); for (const auto& value : *dq.proxyProtocolValues) { result.push_back({ value.type, value.content }); } return result; }); luaCtx.registerFunction("changeName", [](DNSQuestion& dq, const DNSName& newName) -> bool { if (!dnsdist::changeNameInDNSPacket(dq.getMutableData(), dq.ids.qname, newName)) { return false; } dq.ids.qname = newName; return true; }); luaCtx.registerFunction, LuaArray>&, boost::optional)>("spoof", [](DNSQuestion& dnsQuestion, const boost::variant, LuaArray>& response, boost::optional typeForAny) { if (response.type() == typeid(LuaArray)) { std::vector data; auto responses = boost::get>(response); data.reserve(responses.size()); for (const auto& resp : responses) { data.push_back(resp.second); } std::string result; SpoofAction tempSpoofAction(data); tempSpoofAction(&dnsQuestion, &result); return; } if (response.type() == typeid(LuaArray)) { std::vector data; auto responses = boost::get>(response); data.reserve(responses.size()); for (const auto& resp : responses) { data.push_back(resp.second); } std::string result; SpoofAction tempSpoofAction(data, typeForAny ? *typeForAny : std::optional()); tempSpoofAction(&dnsQuestion, &result); return; } }); luaCtx.registerFunction("setEDNSOption", [](DNSQuestion& dq, uint16_t code, const std::string& data) { setEDNSOption(dq, code, data); }); luaCtx.registerFunction& extraText)>("setExtendedDNSError", [](DNSQuestion& dnsQuestion, uint16_t infoCode, const boost::optional& extraText) { EDNSExtendedError ede; ede.infoCode = infoCode; if (extraText) { ede.extraText = *extraText; } dnsQuestion.ids.d_extendedError = std::make_unique(ede); }); luaCtx.registerFunction("suspend", [](DNSQuestion& dq, uint16_t asyncID, uint16_t queryID, uint32_t timeoutMs) { dq.asynchronous = true; return dnsdist::suspendQuery(dq, asyncID, queryID, timeoutMs); }); luaCtx.registerFunction("setRestartable", [](DNSQuestion& dq) { dq.ids.d_packet = std::make_unique(dq.getData()); return true; }); class AsynchronousObject { public: AsynchronousObject(std::unique_ptr&& obj_): object(std::move(obj_)) { } DNSQuestion getDQ() const { return object->getDQ(); } DNSResponse getDR() const { return object->getDR(); } bool resume() { return dnsdist::queueQueryResumptionEvent(std::move(object)); } bool drop() { auto sender = object->getTCPQuerySender(); if (!sender) { return false; } struct timeval now; gettimeofday(&now, nullptr); sender->notifyIOError(now, TCPResponse(std::move(object->query))); return true; } bool setRCode(uint8_t rcode, bool clearAnswers) { return dnsdist::setInternalQueryRCode(object->query.d_idstate, object->query.d_buffer, rcode, clearAnswers); } private: std::unique_ptr object; }; luaCtx.registerFunction("getDQ", [](const AsynchronousObject& obj) { return obj.getDQ(); }); luaCtx.registerFunction("getDR", [](const AsynchronousObject& obj) { return obj.getDR(); }); luaCtx.registerFunction("resume", [](AsynchronousObject& obj) { return obj.resume(); }); luaCtx.registerFunction("drop", [](AsynchronousObject& obj) { return obj.drop(); }); luaCtx.registerFunction("setRCode", [](AsynchronousObject& obj, uint8_t rcode, bool clearAnswers) { return obj.setRCode(rcode, clearAnswers); }); luaCtx.writeFunction("getAsynchronousObject", [](uint16_t asyncID, uint16_t queryID) -> AsynchronousObject { if (!dnsdist::g_asyncHolder) { throw std::runtime_error("Unable to resume, no asynchronous holder"); } auto query = dnsdist::g_asyncHolder->get(asyncID, queryID); if (!query) { throw std::runtime_error("Unable to find asynchronous object"); } return AsynchronousObject(std::move(query)); }); /* LuaWrapper doesn't support inheritance */ luaCtx.registerMember("localaddr", [](const DNSResponse& dq) -> const ComboAddress { return dq.ids.origDest; }, [](DNSResponse& dq, const ComboAddress newLocal) { (void) newLocal; }); luaCtx.registerMember("qname", [](const DNSResponse& dq) -> const DNSName { return dq.ids.qname; }, [](DNSResponse& dq, const DNSName& newName) { (void) newName; }); luaCtx.registerMember("qtype", [](const DNSResponse& dq) -> uint16_t { return dq.ids.qtype; }, [](DNSResponse& dq, uint16_t newType) { (void) newType; }); luaCtx.registerMember("qclass", [](const DNSResponse& dq) -> uint16_t { return dq.ids.qclass; }, [](DNSResponse& dq, uint16_t newClass) { (void) newClass; }); luaCtx.registerMember("rcode", [](const DNSResponse& dq) -> int { return static_cast(dq.getHeader()->rcode); }, [](DNSResponse& dq, int newRCode) { dnsdist::PacketMangling::editDNSHeaderFromPacket(dq.getMutableData(), [newRCode](dnsheader& header) { header.rcode = static_cast(newRCode); return true; }); }); luaCtx.registerMember("remoteaddr", [](const DNSResponse& dq) -> const ComboAddress { return dq.ids.origRemote; }, [](DNSResponse& dq, const ComboAddress newRemote) { (void) newRemote; }); luaCtx.registerMember("dh", [](const DNSResponse& dr) -> dnsheader* { return dr.getMutableHeader(); }, [](DNSResponse& dr, const dnsheader* dh) { dnsdist::PacketMangling::editDNSHeaderFromPacket(dr.getMutableData(), [&dh](dnsheader& header) { header = *dh; return true; }); }); luaCtx.registerMember("len", [](const DNSResponse& dq) -> uint16_t { return dq.getData().size(); }, [](DNSResponse& dq, uint16_t newlen) { dq.getMutableData().resize(newlen); }); luaCtx.registerMember("opcode", [](const DNSResponse& dq) -> uint8_t { return dq.getHeader()->opcode; }, [](DNSResponse& dq, uint8_t newOpcode) { (void) newOpcode; }); luaCtx.registerMember("tcp", [](const DNSResponse& dq) -> bool { return dq.overTCP(); }, [](DNSResponse& dq, bool newTcp) { (void) newTcp; }); luaCtx.registerMember("skipCache", [](const DNSResponse& dq) -> bool { return dq.ids.skipCache; }, [](DNSResponse& dq, bool newSkipCache) { dq.ids.skipCache = newSkipCache; }); luaCtx.registerMember("pool", [](const DNSResponse& dq) -> std::string { return dq.ids.poolName; }, [](DNSResponse& dq, const std::string& newPoolName) { dq.ids.poolName = newPoolName; }); luaCtx.registerFunction editFunc)>("editTTLs", [](DNSResponse& dr, std::function editFunc) { editDNSPacketTTL(reinterpret_cast(dr.getMutableData().data()), dr.getData().size(), editFunc); }); luaCtx.registerFunction("getDO", [](const DNSResponse& dq) { return getEDNSZ(dq) & EDNS_HEADER_FLAG_DO; }); luaCtx.registerFunction("getContent", [](const DNSResponse& dq) { return std::string(reinterpret_cast(dq.getData().data()), dq.getData().size()); }); luaCtx.registerFunction("setContent", [](DNSResponse& dr, const std::string& raw) { uint16_t oldID = dr.getHeader()->id; auto& buffer = dr.getMutableData(); buffer.clear(); buffer.insert(buffer.begin(), raw.begin(), raw.end()); dnsdist::PacketMangling::editDNSHeaderFromPacket(buffer, [oldID](dnsheader& header) { header.id = oldID; return true; }); }); luaCtx.registerFunction(DNSResponse::*)()const>("getEDNSOptions", [](const DNSResponse& dq) { if (dq.ednsOptions == nullptr) { parseEDNSOptions(dq); if (dq.ednsOptions == nullptr) { throw std::runtime_error("parseEDNSOptions should have populated the EDNS options"); } } return *dq.ednsOptions; }); luaCtx.registerFunction("getTrailingData", [](const DNSResponse& dq) { return dq.getTrailingData(); }); luaCtx.registerFunction("setTrailingData", [](DNSResponse& dq, const std::string& tail) { return dq.setTrailingData(tail); }); luaCtx.registerFunction("setTag", [](DNSResponse& dr, const std::string& strLabel, const std::string& strValue) { dr.setTag(strLabel, strValue); }); luaCtx.registerFunction)>("setTagArray", [](DNSResponse& dr, const LuaAssociativeTable&tags) { for (const auto& tag : tags) { dr.setTag(tag.first, tag.second); } }); luaCtx.registerFunction("getTag", [](const DNSResponse& dr, const std::string& strLabel) { if (!dr.ids.qTag) { return string(); } std::string strValue; const auto it = dr.ids.qTag->find(strLabel); if (it == dr.ids.qTag->cend()) { return string(); } return it->second; }); luaCtx.registerFunction("getTagArray", [](const DNSResponse& dr) { if (!dr.ids.qTag) { QTag empty; return empty; } return *dr.ids.qTag; }); luaCtx.registerFunction("getProtocol", [](const DNSResponse& dr) { return dr.getProtocol().toPrettyString(); }); luaCtx.registerFunction("getQueryTime", [](const DNSResponse& dr) { return dr.ids.queryRealTime.getStartTime(); }); luaCtx.registerFunction("sendTrap", [](const DNSResponse& dr, boost::optional reason) { #ifdef HAVE_NET_SNMP if (g_snmpAgent && g_snmpTrapsEnabled) { g_snmpAgent->sendDNSTrap(dr, reason ? *reason : ""); } #endif /* HAVE_NET_SNMP */ }); #ifdef HAVE_DNS_OVER_HTTPS luaCtx.registerFunction("getHTTPPath", [](const DNSQuestion& dq) { if (dq.ids.du == nullptr) { return std::string(); } return dq.ids.du->getHTTPPath(); }); luaCtx.registerFunction("getHTTPQueryString", [](const DNSQuestion& dq) { if (dq.ids.du == nullptr) { return std::string(); } return dq.ids.du->getHTTPQueryString(); }); luaCtx.registerFunction("getHTTPHost", [](const DNSQuestion& dq) { if (dq.ids.du == nullptr) { return std::string(); } return dq.ids.du->getHTTPHost(); }); luaCtx.registerFunction("getHTTPScheme", [](const DNSQuestion& dq) { if (dq.ids.du == nullptr) { return std::string(); } return dq.ids.du->getHTTPScheme(); }); luaCtx.registerFunction(DNSQuestion::*)(void)const>("getHTTPHeaders", [](const DNSQuestion& dq) { if (dq.ids.du == nullptr) { return LuaAssociativeTable(); } return dq.ids.du->getHTTPHeaders(); }); luaCtx.registerFunction contentType)>("setHTTPResponse", [](DNSQuestion& dq, uint64_t statusCode, const std::string& body, const boost::optional contentType) { if (dq.ids.du == nullptr) { return; } checkParameterBound("DNSQuestion::setHTTPResponse", statusCode, std::numeric_limits::max()); PacketBuffer vect(body.begin(), body.end()); dq.ids.du->setHTTPResponse(statusCode, std::move(vect), contentType ? *contentType : ""); }); #endif /* HAVE_DNS_OVER_HTTPS */ luaCtx.registerFunction("setNegativeAndAdditionalSOA", [](DNSQuestion& dq, bool nxd, const std::string& zone, uint64_t ttl, const std::string& mname, const std::string& rname, uint64_t serial, uint64_t refresh, uint64_t retry, uint64_t expire, uint64_t minimum) { checkParameterBound("setNegativeAndAdditionalSOA", ttl, std::numeric_limits::max()); checkParameterBound("setNegativeAndAdditionalSOA", serial, std::numeric_limits::max()); checkParameterBound("setNegativeAndAdditionalSOA", refresh, std::numeric_limits::max()); checkParameterBound("setNegativeAndAdditionalSOA", retry, std::numeric_limits::max()); checkParameterBound("setNegativeAndAdditionalSOA", expire, std::numeric_limits::max()); checkParameterBound("setNegativeAndAdditionalSOA", minimum, std::numeric_limits::max()); return setNegativeAndAdditionalSOA(dq, nxd, DNSName(zone), ttl, DNSName(mname), DNSName(rname), serial, refresh, retry, expire, minimum, false); }); luaCtx.registerFunction& extraText)>("setExtendedDNSError", [](DNSResponse& dnsResponse, uint16_t infoCode, const boost::optional& extraText) { EDNSExtendedError ede; ede.infoCode = infoCode; if (extraText) { ede.extraText = *extraText; } dnsResponse.ids.d_extendedError = std::make_unique(ede); }); luaCtx.registerFunction("suspend", [](DNSResponse& dr, uint16_t asyncID, uint16_t queryID, uint32_t timeoutMs) { dr.asynchronous = true; return dnsdist::suspendResponse(dr, asyncID, queryID, timeoutMs); }); luaCtx.registerFunction("changeName", [](DNSResponse& dr, const DNSName& newName) -> bool { if (!dnsdist::changeNameInDNSPacket(dr.getMutableData(), dr.ids.qname, newName)) { return false; } dr.ids.qname = newName; return true; }); luaCtx.registerFunction("restart", [](DNSResponse& dr) { if (!dr.ids.d_packet) { return false; } dr.asynchronous = true; dr.getMutableData() = *dr.ids.d_packet; auto query = dnsdist::getInternalQueryFromDQ(dr, false); return dnsdist::queueQueryResumptionEvent(std::move(query)); }); luaCtx.registerFunction(DNSResponse::*)(void)const>("getSelectedBackend", [](const DNSResponse& dr) { return dr.d_downstream; }); #endif /* DISABLE_NON_FFI_DQ_BINDINGS */ }