From: Remi Gacogne Date: Thu, 13 Oct 2016 12:17:39 +0000 (+0200) Subject: rec: Pass a `DNSQuestion` object to Lua hooks X-Git-Tag: dnsdist-1.1.0-beta2~14^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ba21fcfedc103f12b6a8eadc17f2797b6d7ffb7b;p=thirdparty%2Fpdns.git rec: Pass a `DNSQuestion` object to Lua hooks The main motivation is to get rid of the huge number of parameters passed to our Lua hooks, and to make it easy to access new values from every hook. It also prevents copying the `DNSName` and `ComboAddress` for every Lua hook. I'm still wondering whether we actually need to make `dq` a shared pointer, or if we couldn't just allocate it on the stack. --- diff --git a/pdns/lua-recursor4.cc b/pdns/lua-recursor4.cc index f4d29ff793..461a8bf7d4 100644 --- a/pdns/lua-recursor4.cc +++ b/pdns/lua-recursor4.cc @@ -36,32 +36,32 @@ RecursorLua4::RecursorLua4(const std::string &fname) throw std::runtime_error("Attempt to load a Lua script in a PowerDNS binary without Lua support"); } -bool RecursorLua4::nxdomain(const ComboAddress& remote,const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector& ret, int& res, bool* variable) +bool RecursorLua4::nxdomain(std::shared_ptr dq, int& res) { return false; } -bool RecursorLua4::nodata(const ComboAddress& remote,const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector& ret, int& res, bool* variable) +bool RecursorLua4::nodata(std::shared_ptr dq, int& res) { return false; } -bool RecursorLua4::postresolve(const ComboAddress& remote,const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector& ret, DNSFilterEngine::Policy* appliedPolicy, std::vector* policyTags, int& res, bool* variable) +bool RecursorLua4::postresolve(std::shared_ptr dq, int& res) { return false; } -bool RecursorLua4::prerpz(const ComboAddress& remote,const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector& res, const vector >* ednsOpts, unsigned int tag, int& ret, bool* wantsRPZ, std::unordered_map* discardedPolicies) +bool RecursorLua4::prerpz(std::shared_ptr dq, int& ret) { return false; } -bool RecursorLua4::preresolve(const ComboAddress& remote, const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector& ret, const vector >* ednsOpts, unsigned int tag, DNSFilterEngine::Policy* appliedPolicy, std::vector* policyTags, int& res, bool* variable, bool* wantsRPZ) +bool RecursorLua4::preresolve(std::shared_ptr dq, int& res) { return false; } -bool RecursorLua4::preoutquery(const ComboAddress& remote, const ComboAddress& local,const DNSName& query, const QType& qtype, bool isTcp, vector& ret, int& res) +bool RecursorLua4::preoutquery(const ComboAddress& ns, const ComboAddress& requestor, const DNSName& query, const QType& qtype, bool isTcp, vector& res, int& ret) { return false; } @@ -159,7 +159,7 @@ static int getFakePTRRecords(const DNSName& qname, const std::string& prefix, ve } -vector > RecursorLua4::DNSQuestion::getEDNSOptions() +vector > RecursorLua4::DNSQuestion::getEDNSOptions() const { if(ednsOptions) return *ednsOptions; @@ -167,7 +167,7 @@ vector > RecursorLua4::DNSQuestion::getEDNSOptions() return vector>(); } -boost::optional RecursorLua4::DNSQuestion::getEDNSOption(uint16_t code) +boost::optional RecursorLua4::DNSQuestion::getEDNSOption(uint16_t code) const { if(ednsOptions) for(const auto& o : *ednsOptions) @@ -177,7 +177,7 @@ boost::optional RecursorLua4::DNSQuestion::getEDNSOption(uint16_t code) return boost::optional(); } -boost::optional RecursorLua4::DNSQuestion::getEDNSSubnet() +boost::optional RecursorLua4::DNSQuestion::getEDNSSubnet() const { if(ednsOptions) { @@ -195,7 +195,7 @@ boost::optional RecursorLua4::DNSQuestion::getEDNSSubnet() } -vector > RecursorLua4::DNSQuestion::getRecords() +vector > RecursorLua4::DNSQuestion::getRecords() const { vector > ret; int num=1; @@ -355,14 +355,18 @@ RecursorLua4::RecursorLua4(const std::string& fname) d_lw->registerFunction("toString", [](const DNSName&dn ) { return dn.toString(); }); d_lw->registerFunction("toStringNoDot", [](const DNSName&dn ) { return dn.toStringNoDot(); }); d_lw->registerFunction("chopOff", [](DNSName&dn ) { return dn.chopOff(); }); - d_lw->registerMember("qname", &DNSQuestion::qname); - d_lw->registerMember("qtype", &DNSQuestion::qtype); - d_lw->registerMember("isTcp", &DNSQuestion::isTcp); - d_lw->registerMember("localaddr", &DNSQuestion::local); - d_lw->registerMember("remoteaddr", &DNSQuestion::remote); + + d_lw->registerMember("qname", [](const DNSQuestion& dq) -> const DNSName& { return dq.qname; }, [](DNSQuestion& dq, const DNSName& newName) { (void) newName; }); + d_lw->registerMember("qtype", [](const DNSQuestion& dq) -> uint16_t { return dq.qtype; }, [](DNSQuestion& dq, uint16_t newType) { (void) newType; }); + d_lw->registerMember("isTcp", [](const DNSQuestion& dq) -> bool { return dq.isTcp; }, [](DNSQuestion& dq, bool newTcp) { (void) newTcp; }); + d_lw->registerMember("localaddr", [](const DNSQuestion& dq) -> const ComboAddress& { return dq.local; }, [](DNSQuestion& dq, const ComboAddress& newLocal) { (void) newLocal; }); + d_lw->registerMember("remoteaddr", [](const DNSQuestion& dq) -> const ComboAddress& { return dq.remote; }, [](DNSQuestion& dq, const ComboAddress& newRemote) { (void) newRemote; }); + + d_lw->registerMember("variable", [](const DNSQuestion& dq) -> bool { return dq.variable; }, [](DNSQuestion& dq, bool newVariable) { dq.variable = newVariable; }); + d_lw->registerMember("wantsRPZ", [](const DNSQuestion& dq) -> bool { return dq.wantsRPZ; }, [](DNSQuestion& dq, bool newWantsRPZ) { dq.wantsRPZ = newWantsRPZ; }); + d_lw->registerMember("rcode", &DNSQuestion::rcode); d_lw->registerMember("tag", &DNSQuestion::tag); - d_lw->registerMember("variable", &DNSQuestion::variable); d_lw->registerMember("followupFunction", &DNSQuestion::followupFunction); d_lw->registerMember("followupPrefix", &DNSQuestion::followupPrefix); d_lw->registerMember("followupName", &DNSQuestion::followupName); @@ -371,7 +375,6 @@ RecursorLua4::RecursorLua4(const std::string& fname) d_lw->registerMember("udpAnswer", &DNSQuestion::udpAnswer); d_lw->registerMember("udpQueryDest", &DNSQuestion::udpQueryDest); d_lw->registerMember("udpCallback", &DNSQuestion::udpCallback); - d_lw->registerMember("wantsRPZ", &DNSQuestion::wantsRPZ); d_lw->registerMember("appliedPolicy", &DNSQuestion::appliedPolicy); d_lw->registerMember("policyName", [](const DNSFilterEngine::Policy& pol) -> std::string { @@ -551,34 +554,39 @@ RecursorLua4::RecursorLua4(const std::string& fname) d_gettag = d_lw->readVariable>("gettag").get_value_or(0); } -bool RecursorLua4::prerpz(const ComboAddress& remote,const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector& res, const vector >* ednsOpts, unsigned int tag, int& ret, bool* wantsRPZ, std::unordered_map* discardedPolicies) +bool RecursorLua4::prerpz(std::shared_ptr dq, int& ret) { - return genhook(d_prerpz, remote, local, query, qtype, isTcp, res, ednsOpts, tag, nullptr, nullptr, ret, nullptr, wantsRPZ, discardedPolicies); + return genhook(d_prerpz, dq, ret); } -bool RecursorLua4::preresolve(const ComboAddress& remote,const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector& res, const vector >* ednsOpts, unsigned int tag, DNSFilterEngine::Policy* appliedPolicy, std::vector* policyTags, int& ret, bool* variable, bool* wantsRPZ) +bool RecursorLua4::preresolve(std::shared_ptr dq, int& ret) { - return genhook(d_preresolve, remote, local, query, qtype, isTcp, res, ednsOpts, tag, appliedPolicy, policyTags, ret, variable, wantsRPZ, nullptr); + return genhook(d_preresolve, dq, ret); } -bool RecursorLua4::nxdomain(const ComboAddress& remote,const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector& res, int& ret, bool* variable) +bool RecursorLua4::nxdomain(std::shared_ptr dq, int& ret) { - return genhook(d_nxdomain, remote, local, query, qtype, isTcp, res, 0, 0, nullptr, nullptr, ret, variable, 0, nullptr); + return genhook(d_nxdomain, dq, ret); } -bool RecursorLua4::nodata(const ComboAddress& remote,const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector& res, int& ret, bool* variable) +bool RecursorLua4::nodata(std::shared_ptr dq, int& ret) { - return genhook(d_nodata, remote, local, query, qtype, isTcp, res, 0, 0, nullptr, nullptr, ret, variable, 0, nullptr); + return genhook(d_nodata, dq, ret); } -bool RecursorLua4::postresolve(const ComboAddress& remote,const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector& res, DNSFilterEngine::Policy* appliedPolicy, std::vector* policyTags, int& ret, bool* variable) +bool RecursorLua4::postresolve(std::shared_ptr dq, int& ret) { - return genhook(d_postresolve, remote, local, query, qtype, isTcp, res, 0, 0, appliedPolicy, policyTags, ret, variable, 0, nullptr); + return genhook(d_postresolve, dq, ret); } bool RecursorLua4::preoutquery(const ComboAddress& ns, const ComboAddress& requestor, const DNSName& query, const QType& qtype, bool isTcp, vector& res, int& ret) { - return genhook(d_preoutquery, ns, requestor, query, qtype, isTcp, res, 0, 0, nullptr, nullptr, ret, 0, 0, nullptr); + bool variableAnswer = false; + bool wantsRPZ = false; + auto dq = std::make_shared(requestor, ns, query, qtype.getCode(), isTcp, variableAnswer, wantsRPZ); + dq->currentRecords = &res; + + return genhook(d_preoutquery, dq, ret); } bool RecursorLua4::ipfilter(const ComboAddress& remote, const ComboAddress& local, const struct dnsheader& dh) @@ -606,28 +614,26 @@ int RecursorLua4::gettag(const ComboAddress& remote, const Netmask& ednssubnet, return 0; } -bool RecursorLua4::genhook(luacall_t& func, const ComboAddress& remote,const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector& res, const vector >* ednsOpts, unsigned int tag, DNSFilterEngine::Policy* appliedPolicy, std::vector* policyTags, int& ret, bool* variable, bool* wantsRPZ, std::unordered_map* discardedPolicies) +bool RecursorLua4::genhook(luacall_t& func, std::shared_ptr dq, int& ret) { if(!func) return false; - auto dq = std::make_shared(); - dq->qname = query; - dq->qtype = qtype.getCode(); - dq->local=local; - dq->remote=remote; - dq->records = res; - dq->tag = tag; - dq->ednsOptions = ednsOpts; - dq->isTcp = isTcp; + if (dq->currentRecords) { + dq->records = *dq->currentRecords; + } else { + dq->records.clear(); + } + + dq->followupFunction.clear(); + dq->followupPrefix.clear(); + dq->followupName.clear(); + dq->udpQuery.clear(); + dq->udpAnswer.clear(); + dq->udpCallback.clear(); + dq->rcode = ret; - dq->policyTags = policyTags; - dq->appliedPolicy = appliedPolicy; - if(wantsRPZ) dq->wantsRPZ = *wantsRPZ; - if(discardedPolicies) dq->discardedPolicies = discardedPolicies; bool handled=func(dq); - if(variable) *variable |= dq->variable; // could still be set to indicate this *name* is variable, even if not 'handled' - if(wantsRPZ) *wantsRPZ = dq->wantsRPZ; // Even if we did not handle the query, RPZ could be disabled if(handled) { loop:; @@ -635,7 +641,7 @@ loop:; if(!dq->followupFunction.empty()) { if(dq->followupFunction=="followCNAMERecords") { - ret = followCNAMERecords(dq->records, qtype); + ret = followCNAMERecords(dq->records, QType(dq->qtype)); } else if(dq->followupFunction=="getFakeAAAARecords") { ret=getFakeAAAARecords(dq->followupName, dq->followupPrefix, dq->records); @@ -650,18 +656,18 @@ loop:; theL()<variable; // could still be set to indicate this *name* is variable - if(!res) { + bool result=func(dq); + if(!result) { return false; } goto loop; } } - res=dq->records; + if (dq->currentRecords) { + *dq->currentRecords = dq->records; + } } - // see if they added followup work for us too return handled; } diff --git a/pdns/lua-recursor4.hh b/pdns/lua-recursor4.hh index a684ee0ae3..c88e33c5c5 100644 --- a/pdns/lua-recursor4.hh +++ b/pdns/lua-recursor4.hh @@ -44,38 +44,38 @@ private: public: explicit RecursorLua4(const std::string& fname); ~RecursorLua4(); // this is so unique_ptr works with an incomplete type - bool prerpz(const ComboAddress& remote,const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector& res, const vector >* ednsOpts, unsigned int tag, int& ret, bool* wantsRPZ, std::unordered_map* discardedPolicies); - bool preresolve(const ComboAddress& remote,const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector& res, const vector >* ednsOpts, unsigned int tag, DNSFilterEngine::Policy* appliedPolicy, std::vector* policyTags, int& ret, bool* variable, bool* wantsRPZ); - bool nxdomain(const ComboAddress& remote, const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector& res, int& ret, bool* variable); - bool nodata(const ComboAddress& remote, const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector& res, int& ret, bool* variable); - bool postresolve(const ComboAddress& remote, const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector& res, DNSFilterEngine::Policy* appliedPolicy, std::vector* policyTags, int& ret, bool* variable); - bool preoutquery(const ComboAddress& ns, const ComboAddress& requestor, const DNSName& query, const QType& qtype, bool isTcp, vector& res, int& ret); - bool ipfilter(const ComboAddress& remote, const ComboAddress& local, const struct dnsheader&); - - int gettag(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& query, uint16_t qtype, std::vector* policyTags); - - typedef std::function > >(ComboAddress, Netmask, ComboAddress, DNSName, uint16_t)> gettag_t; - gettag_t d_gettag; // public so you can query if we have this hooked - -private: struct DNSQuestion { - DNSName qname; - uint16_t qtype; - ComboAddress local, remote; - int rcode{0}; - // struct dnsheader, packet length would be great + DNSQuestion(const ComboAddress& rem, const ComboAddress& loc, const DNSName& query, uint16_t type, bool tcp, bool& variable_, bool& wantsRPZ_): qname(query), qtype(type), local(loc), remote(rem), isTcp(tcp), variable(variable_), wantsRPZ(wantsRPZ_) + { + } + const DNSName& qname; + const uint16_t qtype; + const ComboAddress& local; + const ComboAddress& remote; + const bool isTcp; + const std::vector>* ednsOptions{nullptr}; + vector* currentRecords{nullptr}; + DNSFilterEngine::Policy* appliedPolicy{nullptr}; + std::vector* policyTags{nullptr}; + std::unordered_map* discardedPolicies{nullptr}; + bool& variable; + bool& wantsRPZ; int tag{0}; - vector records; + +#ifdef HAVE_LUA void addAnswer(uint16_t type, const std::string& content, boost::optional ttl, boost::optional name); void addRecord(uint16_t type, const std::string& content, DNSResourceRecord::Place place, boost::optional ttl, boost::optional name); - vector > getRecords(); - vector > getEDNSOptions(); - boost::optional getEDNSOption(uint16_t code); - boost::optional getEDNSSubnet(); + vector > getRecords() const; + vector > getEDNSOptions() const; + boost::optional getEDNSOption(uint16_t code) const; + boost::optional getEDNSSubnet() const; void setRecords(const vector >& records); - bool variable{false}; + + int rcode{0}; + // struct dnsheader, packet length would be great + vector records; string followupFunction; string followupPrefix; @@ -86,19 +86,37 @@ private: string udpCallback; std::unordered_map data; - const std::vector>* ednsOptions; DNSName followupName; - - DNSFilterEngine::Policy* appliedPolicy; - std::vector* policyTags; - std::unordered_map* discardedPolicies; - bool isTcp; - bool wantsRPZ{true}; +#endif }; + int gettag(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& qname, uint16_t qtype, std::vector* policyTags); + + bool prerpz(std::shared_ptr dq, int& ret); + bool preresolve(std::shared_ptr dq, int& ret); + bool nxdomain(std::shared_ptr dq, int& ret); + bool nodata(std::shared_ptr dq, int& ret); + bool postresolve(std::shared_ptr dq, int& ret); + + bool preoutquery(const ComboAddress& ns, const ComboAddress& requestor, const DNSName& query, const QType& qtype, bool isTcp, vector& res, int& ret); + bool ipfilter(const ComboAddress& remote, const ComboAddress& local, const struct dnsheader&); + + bool needDQ() const + { + return (d_prerpz || + d_preresolve || + d_nxdomain || + d_nodata || + d_postresolve); + } + + typedef std::function > >(ComboAddress, Netmask, ComboAddress, DNSName, uint16_t)> gettag_t; + gettag_t d_gettag; // public so you can query if we have this hooked + +private: typedef std::function)> luacall_t; luacall_t d_prerpz, d_preresolve, d_nxdomain, d_nodata, d_postresolve, d_preoutquery, d_postoutquery; - bool genhook(luacall_t& func, const ComboAddress& remote,const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector& res, const vector >* ednsOpts, unsigned int tag, DNSFilterEngine::Policy* appliedPolicy, std::vector* policyTags, int& ret, bool* variable, bool* wantsRPZ, std::unordered_map* discardedPolicies); + bool genhook(luacall_t& func, std::shared_ptr dq, int& ret); typedef std::function ipfilter_t; ipfilter_t d_ipfilter; }; diff --git a/pdns/pdns_recursor.cc b/pdns/pdns_recursor.cc index c719bfe646..75dc2539d5 100644 --- a/pdns/pdns_recursor.cc +++ b/pdns/pdns_recursor.cc @@ -712,6 +712,17 @@ void startDoResolve(void *p) int res; DNSFilterEngine::Policy appliedPolicy; DNSRecord spoofed; + std::shared_ptr dq = nullptr; + if (t_pdl->get() && (*t_pdl)->needDQ()) { + dq = std::make_shared(dc->d_remote, dc->d_local, dc->d_mdp.d_qname, dc->d_mdp.d_qtype, dc->d_tcp, variableAnswer, wantsRPZ); + dq->ednsOptions = &dc->d_ednsOpts; + dq->tag = dc->d_tag; + dq->discardedPolicies = &sr.d_discardedPolicies; + dq->policyTags = &dc->d_policyTags; + dq->appliedPolicy = &appliedPolicy; + dq->currentRecords = &ret; + } + if(dc->d_mdp.d_qtype==QType::ANY && !dc->d_tcp && g_anyToTcp) { pw.getHeader()->tc = 1; res = 0; @@ -741,7 +752,7 @@ void startDoResolve(void *p) sr.setCacheOnly(); if (t_pdl->get()) { - (*t_pdl)->prerpz(dc->d_remote, dc->d_local, dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), dc->d_tcp, ret, dc->d_ednsOpts.empty() ? 0 : &dc->d_ednsOpts, dc->d_tag, res, &wantsRPZ, &sr.d_discardedPolicies); + (*t_pdl)->prerpz(dq, res); } // Check if the query has a policy attached to it @@ -750,7 +761,7 @@ void startDoResolve(void *p) } // if there is a RecursorLua active, and it 'took' the query in preResolve, we don't launch beginResolve - if(!t_pdl->get() || !(*t_pdl)->preresolve(dc->d_remote, dc->d_local, dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), dc->d_tcp, ret, dc->d_ednsOpts.empty() ? 0 : &dc->d_ednsOpts, dc->d_tag, &appliedPolicy, &dc->d_policyTags, res, &variableAnswer, &wantsRPZ)) { + if(!t_pdl->get() || !(*t_pdl)->preresolve(dq, res)) { sr.d_wantsRPZ = wantsRPZ; if(wantsRPZ) { @@ -859,14 +870,14 @@ void startDoResolve(void *p) for(; i!= ret.cend(); ++i) if(i->d_type == dc->d_mdp.d_qtype && i->d_place == DNSResourceRecord::ANSWER) break; - if(i == ret.cend() && (*t_pdl)->nodata(dc->d_remote, dc->d_local, dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), dc->d_tcp, ret, res, &variableAnswer)) + if(i == ret.cend() && (*t_pdl)->nodata(dq, res)) shouldNotValidate = true; } - else if(res == RCode::NXDomain && (*t_pdl)->nxdomain(dc->d_remote, dc->d_local, dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), dc->d_tcp, ret, res, &variableAnswer)) + else if(res == RCode::NXDomain && (*t_pdl)->nxdomain(dq, res)) shouldNotValidate = true; - if((*t_pdl)->postresolve(dc->d_remote, dc->d_local, dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), dc->d_tcp, ret, &appliedPolicy, &dc->d_policyTags, res, &variableAnswer)) + if((*t_pdl)->postresolve(dq, res)) shouldNotValidate = true; }