]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/lua-pdns.cc
Merge pull request #5461 from rgacogne/rec-cache-index
[thirdparty/pdns.git] / pdns / lua-pdns.cc
1 /*
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 #include "lua-pdns.hh"
26 // #include "syncres.hh"
27
28
29 #if !defined(HAVE_LUA)
30
31 // stub implementation
32
33 PowerDNSLua::PowerDNSLua(const std::string& fname)
34 {
35 throw runtime_error("Lua support disabled");
36 }
37
38
39 PowerDNSLua::~PowerDNSLua()
40 {
41
42 }
43
44 #else
45
46 extern "C" {
47 #undef L
48 /* Include the Lua API header files. */
49 #include <lua.h>
50 #include <lauxlib.h>
51 #include <lualib.h>
52 }
53
54 #include <stdlib.h>
55 #include <stdio.h>
56 #include <string>
57 #include <vector>
58 #include <stdexcept>
59 #include "logger.hh"
60 #include "namespaces.hh"
61 #include "dnsparser.hh"
62 #undef L
63
64 bool netmaskMatchTable(lua_State* lua, const std::string& ip)
65 {
66 lua_pushnil(lua); /* first key */
67 while (lua_next(lua, 2) != 0) {
68 string netmask=lua_tostring(lua, -1);
69 Netmask nm(netmask);
70 ComboAddress ca(ip);
71 lua_pop(lua, 1);
72
73 if(nm.match(ip))
74 return true;
75 }
76 return false;
77 }
78
79 static bool getFromTable(lua_State *lua, const std::string &key, std::string& value)
80 {
81 lua_pushstring(lua, key.c_str()); // 4 is now '1'
82 lua_gettable(lua, -2); // replace by the first entry of our table we hope
83
84 bool ret=false;
85 if(!lua_isnil(lua, -1)) {
86 value = lua_tostring(lua, -1);
87 ret=true;
88 }
89 lua_pop(lua, 1);
90 return ret;
91 }
92
93 static bool getFromTable(lua_State *lua, const std::string &key, uint32_t& value)
94 {
95 lua_pushstring(lua, key.c_str()); // 4 is now '1'
96 lua_gettable(lua, -2); // replace by the first entry of our table we hope
97
98 bool ret=false;
99
100 if(!lua_isnil(lua, -1)) {
101 value = (uint32_t)lua_tonumber(lua, -1);
102 ret=true;
103 }
104 lua_pop(lua, 1);
105 return ret;
106 }
107
108
109 void pushLuaTable(lua_State* lua, const vector<pair<string,string>>& table)
110 {
111 lua_newtable(lua);
112 for(const auto& e : table) {
113 lua_pushstring(lua, e.second.c_str());
114 lua_setfield(lua, -2, e.first.c_str());
115 }
116 }
117
118 vector<pair<string,string>> getLuaTable(lua_State* lua, int index)
119 {
120 vector<pair<string,string>> ret;
121 // Push another reference to the table on top of the stack (so we know
122 // where it is, and this function can work for negative, positive and
123 // pseudo indices
124 lua_pushvalue(lua, index);
125 // stack now contains: -1 => table
126 lua_pushnil(lua);
127 // stack now contains: -1 => nil; -2 => table
128 while (lua_next(lua, -2)) {
129 // stack now contains: -1 => value; -2 => key; -3 => table
130 // copy the key so that lua_tostring does not modify the original
131 lua_pushvalue(lua, -2);
132 // stack now contains: -1 => key; -2 => value; -3 => key; -4 => table
133 const char *key = lua_tostring(lua, -1);
134 const char *value = lua_tostring(lua, -2);
135 ret.push_back({key,value});
136 // pop value + copy of key, leaving original key
137 lua_pop(lua, 2);
138 // stack now contains: -1 => key; -2 => table
139 }
140 // stack now contains: -1 => table (when lua_next returns 0 it pops the key
141 // but does not push anything.)
142 // Pop table
143 lua_pop(lua, 1);
144 // Stack is now the same as it was on entry to this function
145 return ret;
146 }
147
148
149 void pushResourceRecordsTable(lua_State* lua, const vector<DNSRecord>& records)
150 {
151 // make a table of tables
152 lua_newtable(lua);
153
154 int pos=0;
155 for(const auto& rr: records)
156 {
157 // row number, used by 'lua_settable' below
158 lua_pushnumber(lua, ++pos);
159 // "row" table
160 lua_newtable(lua);
161
162 lua_pushstring(lua, rr.d_name.toString().c_str());
163 lua_setfield(lua, -2, "qname"); // pushes value at the top of the stack to the table immediately below that (-1 = top, -2 is below)
164
165 lua_pushstring(lua, rr.d_content->getZoneRepresentation().c_str());
166 lua_setfield(lua, -2, "content");
167
168 lua_pushnumber(lua, rr.d_type);
169 lua_setfield(lua, -2, "qtype");
170
171 lua_pushnumber(lua, rr.d_ttl);
172 lua_setfield(lua, -2, "ttl");
173
174 lua_pushnumber(lua, rr.d_place);
175 lua_setfield(lua, -2, "place");
176
177 lua_pushnumber(lua, rr.d_class);
178 lua_setfield(lua, -2, "qclass");
179
180 lua_settable(lua, -3); // pushes the table we just built into the master table at position pushed above
181 }
182 }
183 // override the __index metatable under loglevels to return Logger::Error to account for nil accesses to the loglevels table
184 int loglevels_index(lua_State* lua)
185 {
186 lua_pushnumber(lua, Logger::Error);
187 return 1;
188 }
189 // push the loglevel subtable onto the stack that will eventually be the pdns table
190 void pushSyslogSecurityLevelTable(lua_State* lua)
191 {
192 lua_newtable(lua);
193 // this function takes the global lua_state from the PowerDNSLua constructor and populates it with the syslog enums values
194 lua_pushnumber(lua, Logger::All);
195 lua_setfield(lua, -2, "All");
196 lua_pushnumber(lua, Logger::Alert);
197 lua_setfield(lua, -2, "Alert");
198 lua_pushnumber(lua, Logger::Critical);
199 lua_setfield(lua, -2, "Critical");
200 lua_pushnumber(lua, Logger::Error);
201 lua_setfield(lua, -2, "Error");
202 lua_pushnumber(lua, Logger::Warning);
203 lua_setfield(lua, -2, "Warning");
204 lua_pushnumber(lua, Logger::Notice);
205 lua_setfield(lua, -2, "Notice");
206 lua_pushnumber(lua, Logger::Info);
207 lua_setfield(lua, -2, "Info");
208 lua_pushnumber(lua, Logger::Debug);
209 lua_setfield(lua, -2, "Debug");
210 lua_pushnumber(lua, Logger::None);
211 lua_setfield(lua, -2, "None");
212 lua_createtable(lua, 0, 1);
213 lua_pushcfunction(lua, loglevels_index);
214 lua_setfield(lua, -2, "__index");
215 lua_setmetatable(lua, -2);
216 }
217 int getLuaTableLength(lua_State* lua, int depth)
218 {
219 #ifndef LUA_VERSION_NUM
220 return luaL_getn(lua, 2);
221 #elif LUA_VERSION_NUM < 502
222 return lua_objlen(lua, 2);
223 #else
224 return lua_rawlen(lua, 2);
225 #endif
226 }
227
228 // expects a table at offset 2, and, importantly DOES NOT POP IT from the stack - only the contents
229 void popResourceRecordsTable(lua_State *lua, const DNSName &query, vector<DNSRecord>& ret)
230 {
231 /* get the result */
232 DNSRecord rr;
233 rr.d_name = query;
234 rr.d_place = DNSResourceRecord::ANSWER;
235 rr.d_ttl = 3600;
236
237 int tableLen = getLuaTableLength(lua, 2);
238
239 for(int n=1; n < tableLen + 1; ++n) {
240 lua_pushnumber(lua, n);
241 lua_gettable(lua, 2);
242
243 uint32_t tmpnum=0;
244 if(!getFromTable(lua, "qtype", tmpnum))
245 rr.d_type=QType::A;
246 else
247 rr.d_type=tmpnum;
248
249 if(!getFromTable(lua, "qclass", tmpnum))
250 rr.d_class = QClass::IN;
251 else {
252 rr.d_class = tmpnum;
253 }
254
255
256 string content;
257 getFromTable(lua, "content", content);
258 rr.d_content=DNSRecordContent::mastermake(rr.d_type, rr.d_class, content);
259
260 if(!getFromTable(lua, "ttl", rr.d_ttl))
261 rr.d_ttl=3600;
262
263 string qname;
264 if(getFromTable(lua, "qname", qname))
265 rr.d_name = DNSName(qname);
266 else
267 rr.d_name = query;
268
269 if(!getFromTable(lua, "place", tmpnum))
270 rr.d_place = DNSResourceRecord::ANSWER;
271 else {
272 rr.d_place = static_cast<DNSResourceRecord::Place>(tmpnum);
273 if(rr.d_place > DNSResourceRecord::ADDITIONAL)
274 rr.d_place = DNSResourceRecord::ADDITIONAL;
275 }
276
277
278 /* removes 'value'; keeps 'key' for next iteration */
279 lua_pop(lua, 1); // table
280
281 // cerr<<"Adding content '"<<rr.content<<"' with place "<<(int)rr.d_place<<" \n";
282 ret.push_back(rr);
283 }
284 }
285
286 extern "C" {
287
288 int netmaskMatchLua(lua_State *lua)
289 {
290 bool result=false;
291 if(lua_gettop(lua) >= 2) {
292 string ip=lua_tostring(lua, 1);
293 if(lua_istable(lua, 2)) {
294 result = netmaskMatchTable(lua, ip);
295 }
296 else {
297 for(int n=2 ; n <= lua_gettop(lua); ++n) {
298 string netmask=lua_tostring(lua, n);
299 Netmask nm(netmask);
300 ComboAddress ca(ip);
301
302 result = nm.match(ip);
303 if(result)
304 break;
305 }
306 }
307 }
308
309 lua_pushboolean(lua, result);
310 return 1;
311 }
312
313 int getLocalAddressLua(lua_State* lua)
314 {
315 lua_getfield(lua, LUA_REGISTRYINDEX, "__PowerDNSLua");
316 PowerDNSLua* pl = (PowerDNSLua*)lua_touserdata(lua, -1);
317
318 lua_pushstring(lua, pl->getLocal().toString().c_str());
319 return 1;
320 }
321
322 // called by lua to indicate that this answer is 'variable' and should not be cached
323 int setVariableLua(lua_State* lua)
324 {
325 lua_getfield(lua, LUA_REGISTRYINDEX, "__PowerDNSLua");
326 PowerDNSLua* pl = (PowerDNSLua*)lua_touserdata(lua, -1);
327 pl->setVariable();
328 return 0;
329 }
330
331 int logLua(lua_State *lua)
332 {
333 // get # of arguments from the pdnslog() lua stack
334 // if it is 1, then the old pdnslog(msg) is used, which we keep for posterity and to prevent lua scripts from breaking
335 // if it is >= 2, then we process it as pdnslog(msg, urgencylevel) for more granular logging
336 int argc = lua_gettop(lua);
337 if(argc == 1) {
338 string message=lua_tostring(lua, 1);
339 theL()<<Logger::Error<<"From Lua script: "<<message<<endl;
340 } else if(argc >= 2) {
341 string message=lua_tostring(lua, 1);
342 int urgencylevel = lua_tonumber(lua, 2);
343 theL()<<urgencylevel<<" "<<message<<endl;
344 }
345 return 0;
346 }
347 }
348
349 PowerDNSLua::PowerDNSLua(const std::string& fname)
350 {
351 d_lua = luaL_newstate();
352
353 // create module iputils & load it
354 #if LUA_VERSION_NUM < 502
355 luaopen_iputils(d_lua);
356 #else
357 luaL_requiref(d_lua, "iputils", luaopen_iputils, 1);
358 #endif
359
360 lua_pushcfunction(d_lua, netmaskMatchLua);
361 lua_setglobal(d_lua, "matchnetmask");
362
363 lua_pushcfunction(d_lua, logLua);
364 lua_setglobal(d_lua, "pdnslog");
365
366 lua_newtable(d_lua);
367
368 for(vector<QType::namenum>::const_iterator iter = QType::names.begin(); iter != QType::names.end(); ++iter) {
369 lua_pushnumber(d_lua, iter->second);
370 lua_setfield(d_lua, -2, iter->first.c_str());
371 }
372 lua_pushnumber(d_lua, 0);
373 lua_setfield(d_lua, -2, "NOERROR");
374 lua_pushnumber(d_lua, 1);
375 lua_setfield(d_lua, -2, "FORMERR");
376 lua_pushnumber(d_lua, 2);
377 lua_setfield(d_lua, -2, "SERVFAIL");
378 lua_pushnumber(d_lua, 3);
379 lua_setfield(d_lua, -2, "NXDOMAIN");
380 lua_pushnumber(d_lua, 4);
381 lua_setfield(d_lua, -2, "NOTIMP");
382 lua_pushnumber(d_lua, 5);
383 lua_setfield(d_lua, -2, "REFUSED");
384 // set syslog codes used by Logger/enum Urgency
385 pushSyslogSecurityLevelTable(d_lua);
386 lua_setfield(d_lua, -2, "loglevels");
387 lua_pushnumber(d_lua, PolicyDecision::PASS);
388 lua_setfield(d_lua, -2, "PASS");
389 lua_pushnumber(d_lua, PolicyDecision::DROP);
390 lua_setfield(d_lua, -2, "DROP");
391 lua_pushnumber(d_lua, PolicyDecision::TRUNCATE);
392 lua_setfield(d_lua, -2, "TRUNCATE");
393
394 lua_setglobal(d_lua, "pdns");
395
396 #ifndef LUA_VERSION_NUM
397 luaopen_base(d_lua);
398 luaopen_string(d_lua);
399
400 if(lua_dofile(d_lua, fname.c_str()))
401 #else
402 luaL_openlibs(d_lua);
403 if(luaL_dofile(d_lua, fname.c_str()))
404 #endif
405 throw runtime_error(string("Error loading Lua file '")+fname+"': "+ string(lua_isstring(d_lua, -1) ? lua_tostring(d_lua, -1) : "unknown error"));
406
407 lua_settop(d_lua, 0);
408
409 lua_pushcfunction(d_lua, setVariableLua);
410 lua_setglobal(d_lua, "setvariable");
411
412 lua_pushcfunction(d_lua, getLocalAddressLua);
413 lua_setglobal(d_lua, "getlocaladdress");
414
415 lua_pushlightuserdata(d_lua, (void*)this);
416 lua_setfield(d_lua, LUA_REGISTRYINDEX, "__PowerDNSLua");
417 }
418
419 bool PowerDNSLua::getFromTable(const std::string& key, std::string& value)
420 {
421 return ::getFromTable(d_lua, key, value);
422 }
423
424 bool PowerDNSLua::getFromTable(const std::string& key, uint32_t& value)
425 {
426 return ::getFromTable(d_lua, key, value);
427 }
428
429 PowerDNSLua::~PowerDNSLua()
430 {
431 lua_close(d_lua);
432 }
433
434 #if 0
435 void luaStackDump (lua_State *Lua) {
436 int i;
437 int top = lua_gettop(Lua);
438 for (i = 1; i <= top; i++) { /* repeat for each level */
439 int t = lua_type(Lua, i);
440 switch (t) {
441
442 case LUA_TSTRING: /* strings */
443 printf("`%s'", lua_tostring(Lua, i));
444 break;
445
446 case LUA_TBOOLEAN: /* booleans */
447 printf(lua_toboolean(Lua, i) ? "true" : "false");
448 break;
449
450 case LUA_TNUMBER: /* numbers */
451 printf("%g", lua_tonumber(Lua, i));
452 break;
453
454 default: /* other values */
455 printf("%s", lua_typename(Lua, t));
456 break;
457
458 }
459 printf(" "); /* put a separator */
460 }
461 printf("\n"); /* end the listing */
462 }
463 #endif
464
465 #endif