From 4ea949413c495254acb0bd19335142761c1efc0c Mon Sep 17 00:00:00 2001 From: bert hubert Date: Tue, 20 Jan 2015 22:03:31 +0100 Subject: [PATCH] add ipfilter() lua hook, document it and also preoutquery. Cache which lua functions are defined, shaving a few microseconds per undefined hook. Feed preoutquery the address of the original requestor, which might then be added to an ipset for use by ipfilter. Update our example scripts. --- docs/markdown/recursor/scripting.md | 33 +++++++++++++++- pdns/lua-recursor.cc | 59 +++++++++++++++++++++++++++-- pdns/lua-recursor.hh | 29 +++++++++++++- pdns/pdns_recursor.cc | 11 ++++++ pdns/powerdns-example-script.lua | 25 +++++++++++- pdns/syncres.cc | 2 +- pdns/syncres.hh | 2 +- 7 files changed, 149 insertions(+), 12 deletions(-) diff --git a/docs/markdown/recursor/scripting.md b/docs/markdown/recursor/scripting.md index a8a92da7b..43cf1c78d 100644 --- a/docs/markdown/recursor/scripting.md +++ b/docs/markdown/recursor/scripting.md @@ -37,12 +37,41 @@ is called after the DNS resolution process has run its course, but ended in an ' ### `function nodata ( remoteip, domain, qtype, records )` is just like `nxdomain`, except it gets called when a domain exists, but the requested type does not. This is where one would implement DNS64. Available since version 3.4. -All these functions are passed the IP address of the requester, plus the name and type being requested. In return, these functions indicate if they have taken over the request, or want to let normal proceedings take their course. +### `function ipfilter ( remoteip )` +This hook gets queried immediately after consulting the packet cache, but before +parsing the DNS packet. If this hook returns a non-zero value, the packet is dropped. +However, because this check is after the packet cache, the IP address might still receive answers +that require no packet parsing. -**Warning**: In development versions of the PowerDNS Recursor, versions which were never released except as for testing purposes, these functions had a fourth parameter: localip. This parameter has been replaced by `getlocaladdress()`, for which see below. +With this hook, undesired traffic can be dropped rapidly before using precious CPU cycles +for parsing. + +Available since 3.7. + +**Note**: `remoteip` is passed as an `iputils.ca` type (for which see below). + +### `function preoutquery ( remoteip, domain, qtype )` +This hook is not called in response to a client packet, but fires when the Recursor +wants to talk to an authoritative server. When this hook returns the special result code -3, +the whole DNS client query causing this outquery gets dropped. + +However, this function can also return records like the preresolve query above. + +Within `preoutquery`, `getlocaladdress()` returns the IP address of the original client requestor. + +Available since 3.7. + +**Note**: `remoteip` is passed as an `iputils.ca` type (for which see below). + +## Semantics + +All these functions are passed the IP address of the requester. Most also get passed the name and type being requested. In return, these functions indicate if they have taken over the request, or want to let normal proceedings take their course. If a function has taken over a request, it should return an rcode (usually 0), and specify a table with records to be put in the answer section of a packet. An interesting rcode is NXDOMAIN (3, or `pdns.NXDOMAIN`), which specifies the non-existence of a domain. Returning -1 and an empty table signifies that the function chose not to intervene. +The `ipfilter` and `preoutquery` hooks are different, in that `ipfilter` can only return a true of false value, and +that `preoutquery` can also return -3 to signify that the whole query should be terminated. + A minimal sample script: ``` diff --git a/pdns/lua-recursor.cc b/pdns/lua-recursor.cc index 65b7fc10d..9d08d08f8 100644 --- a/pdns/lua-recursor.cc +++ b/pdns/lua-recursor.cc @@ -37,6 +37,11 @@ bool RecursorLua::preoutquery(const ComboAddress& remote, const ComboAddress& lo return false; } +bool RecursorLua::ipfilter(const ComboAddress& remote, const ComboAddress& local) +{ + return false; +} + #else @@ -142,29 +147,72 @@ int getFakePTRRecords(const std::string& qname, const std::string& prefix, vecto bool RecursorLua::nxdomain(const ComboAddress& remote, const ComboAddress& local,const string& query, const QType& qtype, vector& ret, int& res, bool* variable) { + if(d_nofuncs.nxdomain) + return false; + return passthrough("nxdomain", remote, local, query, qtype, ret, res, variable); } bool RecursorLua::preresolve(const ComboAddress& remote, const ComboAddress& local,const string& query, const QType& qtype, vector& ret, int& res, bool* variable) { + if(d_nofuncs.preresolve) + return false; return passthrough("preresolve", remote, local, query, qtype, ret, res, variable); } bool RecursorLua::nodata(const ComboAddress& remote, const ComboAddress& local,const string& query, const QType& qtype, vector& ret, int& res, bool* variable) { + if(d_nofuncs.nodata) + return false; + return passthrough("nodata", remote, local, query, qtype, ret, res, variable); } bool RecursorLua::postresolve(const ComboAddress& remote, const ComboAddress& local,const string& query, const QType& qtype, vector& ret, int& res, bool* variable) { + if(d_nofuncs.postresolve) + return false; return passthrough("postresolve", remote, local, query, qtype, ret, res, variable); } -bool RecursorLua::preoutquery(const ComboAddress& remote, const ComboAddress& local,const string& query, const QType& qtype, vector& ret, int& res) +bool RecursorLua::preoutquery(const ComboAddress& ns, const ComboAddress& requestor, const string& query, const QType& qtype, vector& ret, int& res) { - return passthrough("preoutquery", remote, local, query, qtype, ret, res, 0); + if(d_nofuncs.preoutquery) + return false; + + return passthrough("preoutquery", ns, requestor, query, qtype, ret, res, 0); } +// returns true to block +bool RecursorLua::ipfilter(const ComboAddress& remote, const ComboAddress& local) +{ + if(d_nofuncs.ipfilter) + return false; + + lua_getglobal(d_lua, "ipfilter"); + if(!lua_isfunction(d_lua, -1)) { + d_nofuncs.regist("ipfilter"); + lua_pop(d_lua, 1); + return false; + } + d_local = local; + + ComboAddress* ca=(ComboAddress*)lua_newuserdata(d_lua, sizeof(ComboAddress)); + *ca=remote; + luaL_getmetatable(d_lua, "iputils.ca"); + lua_setmetatable(d_lua, -2); + + if(lua_pcall(d_lua, 1, 1, 0)) { + string error=string("lua error in 'ipfilter' while processing: ")+lua_tostring(d_lua, -1); + lua_pop(d_lua, 1); + throw runtime_error(error); + return false; + } + + int newres = (int)lua_tonumber(d_lua, 1); + lua_pop(d_lua, 1); + return newres != -1; +} bool RecursorLua::passthrough(const string& func, const ComboAddress& remote, const ComboAddress& local, const string& query, const QType& qtype, vector& ret, @@ -173,7 +221,10 @@ bool RecursorLua::passthrough(const string& func, const ComboAddress& remote, co d_variable = false; lua_getglobal(d_lua, func.c_str()); if(!lua_isfunction(d_lua, -1)) { - // cerr<<"No such function '"<& res, int& ret, bool* variable); bool nodata(const ComboAddress& remote, const ComboAddress& local, const string& query, const QType& qtype, vector& res, int& ret, bool* variable); bool postresolve(const ComboAddress& remote, const ComboAddress& local, const string& query, const QType& qtype, vector& res, int& ret, bool* variable); - bool preoutquery(const ComboAddress& requestor, const ComboAddress& ns, const string& query, const QType& qtype, vector& res, int& ret); - + bool preoutquery(const ComboAddress& ns, const ComboAddress& requestor, const string& query, const QType& qtype, vector& res, int& ret); + bool ipfilter(const ComboAddress& remote, const ComboAddress& local); private: bool passthrough(const string& func, const ComboAddress& remote,const ComboAddress& local, const string& query, const QType& qtype, vector& ret, int& res, bool* variable); + + struct NoFuncs + { + NoFuncs() : preresolve(0), nxdomain(0), nodata(0), postresolve(0), preoutquery(0), ipfilter() + {} + + void regist(const std::string& func) + { + if(func=="preresolve") preresolve=1; + else if(func=="nxdomain") nxdomain=1; + else if(func=="nodata") nodata=1; + else if(func=="postresolve") postresolve=1; + else if(func=="preoutquery") preoutquery=1; + else if(func=="ipfilter") ipfilter=1; + else throw std::runtime_error("Attempting to blacklist unknown Lua function"); + + } + + void reset() + { + *this = NoFuncs(); + } + bool preresolve, nxdomain, nodata, postresolve, preoutquery, ipfilter; + } d_nofuncs; + }; #endif diff --git a/pdns/pdns_recursor.cc b/pdns/pdns_recursor.cc index 8024e9253..c47440a10 100644 --- a/pdns/pdns_recursor.cc +++ b/pdns/pdns_recursor.cc @@ -558,6 +558,7 @@ void startDoResolve(void *p) SyncRes sr(dc->d_now); if(t_pdl) { sr.setLuaEngine(*t_pdl); + sr.d_requestor=dc->d_remote; } bool tracedQuery=false; // we could consider letting Lua know about this too bool variableAnswer = false; @@ -974,6 +975,15 @@ string* doProcessUDPQuestion(const std::string& question, const ComboAddress& fr return 0; } + if(t_pdl->get()) { + if((*t_pdl)->ipfilter(fromaddr, destaddr)) { + if(!g_quiet) + L<getTid()<<"/"<numProcesses()<<"] DROPPED question from "<numProcesses() > g_maxMThreads) { if(!g_quiet) L<getTid()<<"/"<numProcesses()<<"] DROPPED question from "< nameservers, string auth, s_tcpoutqueries++; d_tcpoutqueries++; } - if(d_pdl && d_pdl->preoutquery(*remoteIP, *remoteIP, qname, qtype, lwr.d_result, resolveret)) { + if(d_pdl && d_pdl->preoutquery(*remoteIP, d_requestor, qname, qtype, lwr.d_result, resolveret)) { LOG(prefix< negcache_t; typedef multi_index_container < -- 2.40.0