5) (g)make install
+(use gmake on many BSD variant and Solaris, regular make on Linux)
+
+OPTIONAL LUA SCRIPTING
+----------------------
+To benefit from Lua scripting, as described on
+http://doc.powerdns.com/recursor-scripting.html please compile like this:
+
+$ LUA=1 (g)make
+or even
+
+$ LUA=1 LUA_CPPFLAGS_CONFIG=-I/usr/local/include/lua5.1 LUA_LIBS_CONFIG=-llua5.1\
+ (g)make
+
+Use the _CONFIG settings to point out to PowerDNS where your Lua
+installation resides. PowerDNS supports both Lua 5.0 and 5.1.
+
PLATFORM SPECIFIC NOTES
-----------------------
When compiling on Solaris 8, use:
UNRELEASED
</para>
<para>
- This version contains a small number of fixes, some more important than others:
+ This version contains powerful scripting abilities, allowing operators to modify DNS responses in many
+ interesting ways. Among other things, these abilities can be used to filter out malware domains, to perform
+ load balancing, to comply with legal and other requirements and finally, to implement 'NXDOMAIN' redirection.
</para>
+ <para>
+ It is hoped that the addition of Lua scripting will enable responsible DNS modification for those that need it.
+ </para>
+ <para>
+ For more details about the Lua scripting, which can be modified, loaded and unloaded at runtime, see <xref linkend="recursor-scripting">.
+ Many thanks are due to the #lua irc channel, for excellent near-realtime Lua support. In addition, a number of PowerDNS users have been
+ enthousiastically testing prereleases of the scripting support, and have found and solved many issues.
+ </para>
+ <para>
+ In addition, 3.1.7 fixes a number of bugs:
+ </para>
+
<para>
<itemizedlist>
<listitem>
have taken over the request, or want to let normal proceedings take their course.
</para>
<para>
- 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.
+ 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 <function>pdns.NXDOMAIN</function>), which specifies the non-existence of a domain.
Returning -1 and an empty table signifies that the function chose not to intervene.
</para>
<para>
print ("nxhandler called for: ", ip, domain, qtype)
ret={}
- if qtype ~= 1 then return -1, ret end -- only A records
+ if qtype ~= pdns.A then return -1, ret end -- only A records
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[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
+ ret[1]={qtype=pdns.A, content="127.1.2.3"} -- add IN A 127.1.2.3
+ ret[2]={qtype=pdns.A, content="127.3.2.1"} -- add IN A 127.3.2.1
return 0, ret -- return no error, plus records
end
</screen>
</warning>
</para>
<para>
- In this sample, the numerical identifier of the A record (1) is used. Later versions of PowerDNS may support a model where labels can be described non-numerically.
- Additionally, the answer content format is (nearly) identical to the storage in the PowerDNS Authoritative Server database, or as in zone files.
- The exception is that, unlike in the datbase, there is no 'prio' field, which means that an MX record with priority 25 pointing to 'smtp.mailserver.com' would be encoded as
+ Note that the domain is passed to the Lua function terminated by a '.'.
+ A more complete sample script is provided as <filename>powerdns-example-script.lua</filename> in the PowerDNS Recursor distribution.
+ </para>
+ <para>
+ The answer content format is (nearly) identical to the storage in the PowerDNS Authoritative Server database, or as in zone files.
+ The exception is that, unlike in the database, there is no 'prio' field, which means that an MX record with priority 25 pointing to 'smtp.mailserver.com' would be encoded as
'25 smtp.mailserver.com.'.
</para>
<para>
- Useful return 'rcodes' include 0 for "no error" and 3 for "NXDOMAIN".
+ Useful return 'rcodes' include 0 for "no error" and <function>pdns.NXDOMAIN</function> for "NXDOMAIN".
</para>
<para>
- Useful fields that can be set in the return table include:
+ Fields that can be set in the return table include:
<variablelist>
<varlistentry>
<term>content</term>
<term>qtype</term>
<listitem>
<para>
- Currently the numerical qtype of the answer, defaulting to '1' which is an A record. '2' is an NS record, '5' a CNAME and '15' an MX record.
+ Currently the numerical qtype of the answer, defaulting to '1' which is an A record. Can be also be specified as
+ <function>pdns.A</function>, or <function>pdns.CNAME</function> etc.
</para>
</listitem>
</varlistentry>
extern "C" int netmaskMatchLua(lua_State *lua)
{
bool result=false;
- if(lua_gettop(lua) == 2) {
+ if(lua_gettop(lua) >= 2) {
string ip=lua_tostring(lua, 1);
- string netmask=lua_tostring(lua, 2);
-
- Netmask nm(netmask);
- ComboAddress ca(ip);
-
- result = nm.match(ip);
+ for(int n=2 ; n <= lua_gettop(lua); ++n) {
+ string netmask=lua_tostring(lua, n);
+
+ Netmask nm(netmask);
+ ComboAddress ca(ip);
+
+ result = nm.match(ip);
+ if(result)
+ break;
+
+ }
}
lua_pushboolean(lua, result);
return 1;
lua_pushcfunction(d_lua, netmaskMatchLua);
lua_setglobal(d_lua, "matchnetmask");
+ lua_newtable(d_lua);
+
+ for(vector<QType::namenum>::const_iterator iter = QType::names.begin(); iter != QType::names.end(); ++iter) {
+ lua_pushnumber(d_lua, iter->second);
+ lua_setfield(d_lua, -2, iter->first.c_str());
+ }
+ lua_pushnumber(d_lua, 3);
+ lua_setfield(d_lua, -2, "NXDOMAIN");
+ lua_setglobal(d_lua, "pdns");
+
}
bool PowerDNSLua::nxdomain(const ComboAddress& remote, const string& query, const QType& qtype, vector<DNSResourceRecord>& ret, int& res)
if domain == "www.ds9c.nl."
then
ret={}
- ret[1]= {qtype=1, content="9.8.7.6", ttl=3601}
- ret[2]= {qtype=1, content="1.2.3.4", ttl=3601}
+ ret[1]= {qtype=pdns.A, content="9.8.7.6", ttl=3601}
+ ret[2]= {qtype=pdns.A, content="1.2.3.4", ttl=3601}
print "dealing!"
return 0, ret
elseif domain == "www.baddomain.com."
then
print "dealing - faking nx"
- return 3, {}
+ return pdns.NXDOMAIN, {}
elseif domain == "echo."
then
print "dealing with echo!"
- return 0, {{qtype=1, content=ip}}
+ return 0, {{qtype=pdns.A, content=ip}}
elseif domain == "echo6."
then
print "dealing with echo6!"
- return 0, {{qtype=28, content=ip}}
+ return 0, {{qtype=pdns.AAAA, content=ip}}
else
print "not dealing!"
return -1, {}
end
function nxdomain ( ip, domain, qtype )
- print ("nxhandler called for: ", ip, domain, qtype)
- if qtype ~= 1 then return -1, {} end -- only A records
+ print ("nxhandler called for: ", ip, domain, qtype, pdns.AAAA)
+ if qtype ~= pdns.A 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")
+ if matchnetmask(ip, "10.0.0.0/8", "192.168.0.0/16", "172.16.0.0/12", "::/0")
then
print "dealing"
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]={qtype=pdns.CNAME, content="www.webserver.com", ttl=3602}
+ ret[2]={qname="www.webserver.com", qtype=pdns.A, content="1.2.3.4", ttl=3602}
+ ret[3]={qname="webserver.com", qtype=pdns.NS, content="ns1.webserver.com", place=2}
-- ret[1]={15, "25 ds9a.nl", 3602}
return 0, ret
else
enum typeenum {A=1,NS=2,CNAME=5,SOA=6, MR=9, PTR=12,HINFO=13,MX=15,TXT=16,RP=17,AFSDB=18,KEY=25,AAAA=28,LOC=29,SRV=33,NAPTR=35, KX=36,
CERT=37,OPT=41, DS=43, SSHDP=44, IPSECKEY=45, RRSIG=46, NSEC=47, DNSKEY=48, DHCID=49,
SPF=99, AXFR=252, IXFR=251, ANY=255, URL=256, MBOXFW=257, CURL=258, ADDR=259} types;
+ typedef pair<string,int> namenum;
+ static vector<namenum> names;
private:
short int code;
- typedef pair<string,int> namenum;
void insert(const char *p, int n);
- static vector<namenum> names;
+
static bool uninit;
};