<sect2>
<title>Configuring Lua scripts</title>
<para>
- In order to load scripts, the PowerDNS Recursor must have 'lua' support built in. The packages distributed from the PowerDNS website have this language
- enabled, other distributions may differ.
+ In order to load scripts, the PowerDNS Recursor must have Lua support built in. The packages distributed from the PowerDNS website have this language
+ enabled, other distributions may differ. To compile with Lua support, use: <literal>LUA=1 make</literal> or <literal>LUA=1 gmake</literal>
+ as the case may be. Paths to the Lua include files and binaries may be found near the top of the <filename>Makefile</filename>.
</para>
<para>
If lua support is available, a script can be configured either via the configuration file, or at runtime via the <command>rec_control</command> tool.
</sect2>
<sect2><title>Writing Lua PowerDNS Recursor scripts</title>
<para>
- Once a script is loaded, PowerDNS looks for two functions: <function>prequery</function> and <function>nxdomain</function>. Either or both of these
+ Once a script is loaded, PowerDNS looks for two functions: <function>preresolve</function> and <function>nxdomain</function>. Either or both of these
can be absent, in which case the corresponding functionality is disabled.
</para>
<para>
- <function>prequery</function> is called before any DNS resolution is attempted, and if this function indicates it, it can supply a direct answer to the
+ <function>preresolve</function> is called before any DNS resolution is attempted, and if this function indicates it, it can supply a direct answer to the
DNS query, overriding the internet. This is useful to combat botnets, or to disable domains unacceptable to an organization for whatever reason.
</para>
<para>
ret={}
if qtype ~= 1 then return -1, ret end -- only A records
- if not string.gmatch(domain, "^www.") then return -1, ret end -- only things that start with www.
+ if not string.find(domain, "^www.") then return -1, ret end -- only things that start with www.
if not matchnetmask(ip, "10.0.0.0/8") then return -1, ret end -- only interfere with local queries
- ret[0]={qtype=1, content="127.1.2.3"} -- add IN A 127.1.2.3
- ret[1]={qtype=1, content="127.3.2.1"} -- add IN A 127.3.2.1
- return 0, ret -- return true, plus records
+ ret[1]={qtype=1, content="127.1.2.3"} -- add IN A 127.1.2.3
+ ret[2]={qtype=1, content="127.3.2.1"} -- add IN A 127.3.2.1
+ return 0, ret -- return no error, plus records
end
</screen>
</para>
- <para>
- For Lua 5.0, use <function>string.find</function> instead of <function>string.gmatch</function>.
- </para>
<para>
<warning>
<para>
<listitem>
<para>
Time to live of a record. Defaults to 3600. Be sure not to specify differing TTLs within answers with an identical qname. While this
- can be encoded in DNS, results may be undesired.
+ will be encoded in DNS, actual results may be undesired.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
+ <para>
+ <warning>
+ <para>
+ The result table must have indexes that start at 1! Otherwise the first or confusingly the last entry of the table will
+ be ignored. A useful technique is to return data using:
+ <literal>return 0, {{qtype=1, content="1.2.3.4"}, {qtype=1, content="4.3.2.1"}}</literal> as this will get the numbering
+ right automatically.
+ </para>
+ </warning>
+ </para>
</sect2>
</sect1>
<sect1 id="recursor-design-and-engineering">
return passthrough("nxdomain", remote, query, qtype, ret, res);
}
-bool PowerDNSLua::prequery(const ComboAddress& remote, const string& query, const QType& qtype, vector<DNSResourceRecord>& ret, int& res)
+bool PowerDNSLua::preresolve(const ComboAddress& remote, const string& query, const QType& qtype, vector<DNSResourceRecord>& ret, int& res)
{
- return passthrough("prequery", remote, query, qtype, ret, res);
+ return passthrough("preresolve", remote, query, qtype, ret, res);
}
bool PowerDNSLua::getFromTable(const std::string& key, std::string& value)
throw runtime_error(error);
return false;
}
- res = (int)lua_tonumber(d_lua, 1); // new rcode
- if(res < 0) {
+ int newres = (int)lua_tonumber(d_lua, 1); // new rcode
+ if(newres < 0) {
// cerr << "handler did not handle"<<endl;
lua_pop(d_lua, 2);
return false;
}
+ res=newres;
/* get the result */
DNSResourceRecord rr;
/* 1 2 3 4 */
/* stack: boolean table key row */
- lua_pushnil(d_lua); /* first key */
- while (lua_next(d_lua, 2) != 0) {
+#ifndef LUA_VERSION_NUM
+ int tableLen = luaL_getn(d_lua, 2);
+#else
+ int tableLen = lua_objlen(d_lua, 2);
+#endif
+
+ for(int n=1; n < tableLen + 1; ++n) {
+ lua_pushnumber(d_lua, n);
+ lua_gettable(d_lua, 2);
uint32_t tmpnum;
if(!getFromTable("qtype", tmpnum))
/* removes 'value'; keeps 'key' for next iteration */
lua_pop(d_lua, 1); // table
- // cerr<<"Adding content '"<<rr.content<<"'\n";
+ // cerr<<"Adding content '"<<rr.content<<"' with place "<<(int)rr.d_place<<" \n";
ret.push_back(rr);
}
explicit PowerDNSLua(const std::string& fname);
~PowerDNSLua();
void reload();
- bool prequery(const ComboAddress& remote, const string& query, const QType& qtype, vector<DNSResourceRecord>& res, int& ret);
+ bool preresolve(const ComboAddress& remote, const string& query, const QType& qtype, vector<DNSResourceRecord>& res, int& ret);
bool nxdomain(const ComboAddress& remote, const string& query, const QType& qtype, vector<DNSResourceRecord>& res, int& ret);
private:
lua_State* d_lua;
int res;
- if(!g_pdl.get() || !g_pdl->prequery(dc->d_remote, dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), ret, res)) {
+ if(!g_pdl.get() || !g_pdl->preresolve(dc->d_remote, dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), ret, res)) {
res = sr.beginResolve(dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), dc->d_mdp.d_qclass, ret);
- if(g_pdl.get() && (res < 0 || res == RCode::NXDomain || res == RCode::ServFail)) {
- g_pdl->nxdomain(dc->d_remote, dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), ret, res);
+ if(g_pdl.get()) {
+ if(res == RCode::NXDomain)
+ g_pdl->nxdomain(dc->d_remote, dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), ret, res);
}
}
-function prequery ( ip, domain, qtype )
+function preresolve ( ip, domain, qtype )
print ("prequery handler called for: ", ip, domain, qtype)
- ret = {}
-
--- ret[1]= {1, "10.11.12.13", 3601};
--- ret[2]= {1, "11.12.13.14", 3601};
+
if domain == "www.ds9c.nl."
then
- ret[0]= {qtype=1, content="9.8.7.6", ttl=3601}
- ret[1]= {qtype=1, content="1.2.3.4", ttl=3601}
+ ret={}
+ ret[1]= {qtype=1, content="9.8.7.6", ttl=3601}
+ ret[2]= {qtype=1, content="1.2.3.4", ttl=3601}
print "dealing!"
return 0, ret
elseif domain == "www.baddomain.com."
then
- print "dealing - nx"
- return 3, ret
+ print "dealing - faking nx"
+ return 3, {}
+ elseif domain == "echo."
+ then
+ print "dealing with echo!"
+ return 0, {{qtype=1, content=ip}}
+ elseif domain == "echo6."
+ then
+ print "dealing with echo6!"
+ return 0, {{qtype=28, content=ip}}
else
print "not dealing!"
- return -1, ret
+ return -1, {}
end
end
function nxdomain ( ip, domain, qtype )
print ("nxhandler called for: ", ip, domain, qtype)
- ret={}
- if qtype ~= 1 then return false, ret end -- only A records
--- if not string.match(domain, "^www.") then return false, ret end -- only things that start with www.
+ if qtype ~= 1 then return -1, {} end -- only A records
+ if not string.find(domain, "^www.") then return -1, {} end -- only things that start with www.
if matchnetmask(ip, "127.0.0.1/8")
then
print "dealing"
- ret[0]={qtype="5", content="www.webserver.com", ttl=3602}
- ret[1]={qname="www.webserver.com", qtype="1", content="1.2.3.4", ttl=3602}
- ret[2]={qname="webserver.com", qtype="2", content="ns1.webserver.com", place=2}
+ ret={}
+ ret[1]={qtype="5", content="www.webserver.com", ttl=3602}
+ ret[2]={qname="www.webserver.com", qtype="1", content="1.2.3.4", ttl=3602}
+ ret[3]={qname="webserver.com", qtype="2", content="ns1.webserver.com", place=2}
-- ret[1]={15, "25 ds9a.nl", 3602}
return 0, ret
else