]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/rec-lua-conf.cc
rec: Fix 'logQuery' compilation without protobuf support
[thirdparty/pdns.git] / pdns / rec-lua-conf.cc
CommitLineData
6a99b9a4 1#include "config.h"
3e61e7f7 2#include "ext/luawrapper/include/LuaContext.hpp"
6a99b9a4 3
3e61e7f7 4#include <fstream>
ad42489c 5#include <thread>
3e61e7f7 6#include "namespaces.hh"
7#include "logger.hh"
f3c18728 8#include "rec-lua-conf.hh"
3e61e7f7 9#include "sortlist.hh"
ad42489c 10#include "filterpo.hh"
11#include "syncres.hh"
12#include "rpzloader.hh"
98c9ec39 13#include "base64.hh"
aa7929a3 14#include "remote_logger.hh"
52ad9eea 15#include "validate.hh"
e48c6b8a 16#include "validate-recursor.hh"
f2234140 17#include "root-dnssec.hh"
3e61e7f7 18
12ce523e 19GlobalStateHolder<LuaConfigItems> g_luaconfs;
f3c18728 20
21/* SO HOW DOES THIS WORK! AND PLEASE PAY ATTENTION!
22 This function can be called at any time. It is expected to overwrite all the contents
23 of LuaConfigItems, which is held in a GlobalStateHolder for RCU properties.
24
25 This function can be called again at a later date, so you must make sure that anything you
26 allow to be configured from here lives in g_luaconfs AND NOWHERE ELSE.
27
28 If someone loads an empty Lua file, the default LuaConfigItems struct MUST MAKE SENSE.
29
30 To make this easy on you, here is a LuaConfigItems constructor where you
31 can set sane defaults:
32*/
33
34LuaConfigItems::LuaConfigItems()
35{
03bf7abb 36 DNSName root("."); // don't use g_rootdnsname here, it might not exist yet
f2234140
PL
37 for (const auto &dsRecord : rootDSs) {
38 auto ds=unique_ptr<DSRecordContent>(dynamic_cast<DSRecordContent*>(DSRecordContent::make(dsRecord)));
03bf7abb 39 dsAnchors[root].insert(*ds);
f2234140 40 }
f3c18728 41}
42
ad42489c 43/* DID YOU READ THE STORY ABOVE? */
44
45template <typename C>
46typename C::value_type::second_type constGet(const C& c, const std::string& name)
47{
48 auto iter = c.find(name);
49 if(iter == c.end())
50 return 0;
51 return iter->second;
52}
53
6a99b9a4 54
8f618901
RG
55static void parseRPZParameters(const std::unordered_map<string,boost::variant<uint32_t, string> >& have, std::string& polName, boost::optional<DNSFilterEngine::Policy>& defpol, uint32_t& maxTTL, size_t& zoneSizeHint)
56{
57 if(have.count("policyName")) {
58 polName = boost::get<std::string>(constGet(have, "policyName"));
59 }
60 if(have.count("defpol")) {
61 defpol=DNSFilterEngine::Policy();
62 defpol->d_kind = (DNSFilterEngine::PolicyKind)boost::get<uint32_t>(constGet(have, "defpol"));
63 defpol->d_name = std::make_shared<std::string>(polName);
64 if(defpol->d_kind == DNSFilterEngine::PolicyKind::Custom) {
65 defpol->d_custom=
8f618901
RG
66 DNSRecordContent::mastermake(QType::CNAME, 1,
67 boost::get<string>(constGet(have,"defcontent"))
6177a176 68 );
8f618901
RG
69
70 if(have.count("defttl"))
71 defpol->d_ttl = static_cast<int32_t>(boost::get<uint32_t>(constGet(have, "defttl")));
72 else
73 defpol->d_ttl = -1; // get it from the zone
74 }
75 }
76 if(have.count("maxTTL")) {
77 maxTTL = boost::get<uint32_t>(constGet(have, "maxTTL"));
78 }
79 if(have.count("zoneSizeHint")) {
80 zoneSizeHint = static_cast<size_t>(boost::get<uint32_t>(constGet(have, "zoneSizeHint")));
81 }
82}
83
f1c7929a
RG
84typedef std::unordered_map<std::string, boost::variant<bool, uint64_t, std::string> > protobufOptions_t;
85
86static void parseProtobufOptions(boost::optional<protobufOptions_t> vars, ProtobufExportConfig& config)
87{
88 if (!vars) {
89 return;
90 }
91
92 if (vars->count("timeout")) {
93 config.timeout = boost::get<uint64_t>((*vars)["timeout"]);
94 }
95
96 if (vars->count("maxQueuedEntries")) {
97 config.maxQueuedEntries = boost::get<uint64_t>((*vars)["maxQueuedEntries"]);
98 }
99
100 if (vars->count("reconnectWaitTime")) {
101 config.reconnectWaitTime = boost::get<uint64_t>((*vars)["reconnectWaitTime"]);
102 }
103
104 if (vars->count("asyncConnect")) {
105 config.asyncConnect = boost::get<bool>((*vars)["asyncConnect"]);
106 }
107
108 if (vars->count("taggedOnly")) {
109 config.taggedOnly = boost::get<bool>((*vars)["taggedOnly"]);
110 }
111
112 if (vars->count("logQueries")) {
113 config.logQueries = boost::get<bool>((*vars)["logQueries"]);
114 }
115
116 if (vars->count("logResponses")) {
117 config.logResponses = boost::get<bool>((*vars)["logResponses"]);
118 }
119}
120
e6ec15bf 121void loadRecursorLuaConfig(const std::string& fname, luaConfigDelayedThreads& delayedThreads)
3e61e7f7 122{
f3c18728 123 LuaConfigItems lci;
124
3e61e7f7 125 LuaContext Lua;
126 if(fname.empty())
127 return;
128 ifstream ifs(fname);
0f5785a6
PL
129 if(!ifs)
130 throw PDNSException("Cannot open file '"+fname+"': "+strerror(errno));
131
63341e8d
RG
132 auto luaconfsLocal = g_luaconfs.getLocal();
133 lci.generation = luaconfsLocal->generation + 1;
134
f3c18728 135 Lua.writeFunction("clearSortlist", [&lci]() { lci.sortlist.clear(); });
3e61e7f7 136
137 /* we can get: "1.2.3.4"
138 {"1.2.3.4", "4.5.6.7"}
139 {"1.2.3.4", {"4.5.6.7", "8.9.10.11"}}
140 */
141
ad42489c 142 map<string,DNSFilterEngine::PolicyKind> pmap{
143 {"NoAction", DNSFilterEngine::PolicyKind::NoAction},
144 {"Drop", DNSFilterEngine::PolicyKind::Drop},
145 {"NXDOMAIN", DNSFilterEngine::PolicyKind::NXDOMAIN},
146 {"NODATA", DNSFilterEngine::PolicyKind::NODATA},
147 {"Truncate", DNSFilterEngine::PolicyKind::Truncate},
148 {"Custom", DNSFilterEngine::PolicyKind::Custom}
149 };
150 Lua.writeVariable("Policy", pmap);
151
8f618901 152 Lua.writeFunction("rpzFile", [&lci](const string& filename, const boost::optional<std::unordered_map<string,boost::variant<uint32_t, string>>>& options) {
ad42489c 153 try {
563bbdca
RG
154 boost::optional<DNSFilterEngine::Policy> defpol;
155 std::string polName("rpzFile");
6b972d59 156 std::shared_ptr<DNSFilterEngine::Zone> zone = std::make_shared<DNSFilterEngine::Zone>();
563bbdca
RG
157 uint32_t maxTTL = std::numeric_limits<uint32_t>::max();
158 if(options) {
159 auto& have = *options;
8f618901
RG
160 size_t zoneSizeHint = 0;
161 parseRPZParameters(have, polName, defpol, maxTTL, zoneSizeHint);
162 if (zoneSizeHint > 0) {
6b972d59 163 zone->reserve(zoneSizeHint);
a2d0450e 164 }
563bbdca 165 }
e6a9dde5 166 g_log<<Logger::Warning<<"Loading RPZ from file '"<<filename<<"'"<<endl;
6b972d59
RG
167 zone->setName(polName);
168 loadRPZFromFile(filename, zone, defpol, maxTTL);
169 lci.dfe.addZone(zone);
e6a9dde5 170 g_log<<Logger::Warning<<"Done loading RPZ from file '"<<filename<<"'"<<endl;
ad42489c 171 }
6791663c 172 catch(const std::exception& e) {
e6a9dde5 173 g_log<<Logger::Error<<"Unable to load RPZ zone from '"<<filename<<"': "<<e.what()<<endl;
ad42489c 174 }
175 });
176
e6ec15bf 177 Lua.writeFunction("rpzMaster", [&lci, &delayedThreads](const boost::variant<string, std::vector<std::pair<int, string> > >& masters_, const string& zoneName, const boost::optional<std::unordered_map<string,boost::variant<uint32_t, string>>>& options) {
209955c7 178
179 boost::optional<DNSFilterEngine::Policy> defpol;
180 std::shared_ptr<DNSFilterEngine::Zone> zone = std::make_shared<DNSFilterEngine::Zone>();
181 TSIGTriplet tt;
182 uint32_t refresh=0;
183 size_t maxReceivedXFRMBytes = 0;
ea448a77 184 uint16_t axfrTimeout = 20;
209955c7 185 uint32_t maxTTL = std::numeric_limits<uint32_t>::max();
186 ComboAddress localAddress;
5f311886
RG
187 std::vector<ComboAddress> masters;
188 if (masters_.type() == typeid(string)) {
189 masters.push_back(ComboAddress(boost::get<std::string>(masters_), 53));
190 }
191 else {
192 for (const auto& master : boost::get<std::vector<std::pair<int, std::string>>>(masters_)) {
193 masters.push_back(ComboAddress(master.second, 53));
194 }
195 }
196
209955c7 197 size_t zoneIdx;
23f886c0 198 std::string dumpFile;
a66c5cfa 199 std::shared_ptr<SOARecordContent> sr = nullptr;
209955c7 200
ad42489c 201 try {
a66c5cfa 202 std::string seedFile;
6b972d59 203 std::string polName(zoneName);
a66c5cfa
RG
204
205 if (options) {
563bbdca 206 auto& have = *options;
8f618901
RG
207 size_t zoneSizeHint = 0;
208 parseRPZParameters(have, polName, defpol, maxTTL, zoneSizeHint);
209 if (zoneSizeHint > 0) {
6b972d59 210 zone->reserve(zoneSizeHint);
a2d0450e 211 }
a66c5cfa 212
563bbdca 213 if(have.count("tsigname")) {
98c9ec39 214 tt.name=DNSName(toLower(boost::get<string>(constGet(have, "tsigname"))));
215 tt.algo=DNSName(toLower(boost::get<string>(constGet(have, "tsigalgo"))));
216 if(B64Decode(boost::get<string>(constGet(have, "tsigsecret")), tt.secret))
217 throw std::runtime_error("TSIG secret is not valid Base-64 encoded");
218 }
a66c5cfa 219
98c9ec39 220 if(have.count("refresh")) {
8f618901 221 refresh = boost::get<uint32_t>(constGet(have,"refresh"));
98c9ec39 222 }
a66c5cfa 223
db8f9152 224 if(have.count("maxReceivedMBytes")) {
8f618901 225 maxReceivedXFRMBytes = static_cast<size_t>(boost::get<uint32_t>(constGet(have,"maxReceivedMBytes")));
db8f9152 226 }
a66c5cfa 227
f6a8f7d7
PL
228 if(have.count("localAddress")) {
229 localAddress = ComboAddress(boost::get<string>(constGet(have,"localAddress")));
230 }
a66c5cfa 231
ea448a77
PL
232 if(have.count("axfrTimeout")) {
233 axfrTimeout = static_cast<uint16_t>(boost::get<uint32_t>(constGet(have, "axfrTimeout")));
234 }
a66c5cfa
RG
235
236 if(have.count("seedFile")) {
237 seedFile = boost::get<std::string>(constGet(have, "seedFile"));
238 }
23f886c0
RG
239
240 if(have.count("dumpFile")) {
241 dumpFile = boost::get<std::string>(constGet(have, "dumpFile"));
242 }
563bbdca 243 }
a66c5cfa 244
5f311886
RG
245 if (localAddress != ComboAddress()) {
246 // We were passed a localAddress, check if its AF matches the masters'
247 for (const auto& master : masters) {
248 if (localAddress.sin4.sin_family != master.sin4.sin_family) {
249 throw PDNSException("Master address("+master.toString()+") is not of the same Address Family as the local address ("+localAddress.toString()+").");
250 }
251 }
6791663c
RG
252 }
253
a66c5cfa
RG
254 DNSName domain(zoneName);
255 zone->setDomain(domain);
6b972d59 256 zone->setName(polName);
6791663c 257 zone->setRefresh(refresh);
209955c7 258 zoneIdx = lci.dfe.addZone(zone);
a66c5cfa
RG
259
260 if (!seedFile.empty()) {
23f886c0
RG
261 g_log<<Logger::Info<<"Pre-loading RPZ zone "<<zoneName<<" from seed file '"<<seedFile<<"'"<<endl;
262 try {
263 sr = loadRPZFromFile(seedFile, zone, defpol, maxTTL);
a66c5cfa 264
23f886c0
RG
265 if (zone->getDomain() != domain) {
266 throw PDNSException("The RPZ zone " + zoneName + " loaded from the seed file (" + zone->getDomain().toString() + ") does not match the one passed in parameter (" + domain.toString() + ")");
267 }
268
269 if (sr == nullptr) {
270 throw PDNSException("The RPZ zone " + zoneName + " loaded from the seed file (" + zone->getDomain().toString() + ") has no SOA record");
271 }
272 }
273 catch(const std::exception& e) {
274 g_log<<Logger::Warning<<"Unable to pre-load RPZ zone "<<zoneName<<" from seed file '"<<seedFile<<"': "<<e.what()<<endl;
a66c5cfa
RG
275 }
276 }
ad42489c 277 }
6791663c 278 catch(const std::exception& e) {
e6a9dde5 279 g_log<<Logger::Error<<"Problem configuring 'rpzMaster': "<<e.what()<<endl;
209955c7 280 exit(1); // FIXME proper exit code?
ad42489c 281 }
6791663c 282 catch(const PDNSException& e) {
e6a9dde5 283 g_log<<Logger::Error<<"Problem configuring 'rpzMaster': "<<e.reason<<endl;
209955c7 284 exit(1); // FIXME proper exit code?
ad42489c 285 }
286
e6ec15bf 287 delayedThreads.rpzMasterThreads.push_back(std::make_tuple(masters, defpol, maxTTL, zoneIdx, tt, maxReceivedXFRMBytes, localAddress, axfrTimeout, sr, dumpFile));
ad42489c 288 });
289
3e61e7f7 290 typedef vector<pair<int,boost::variant<string, vector<pair<int, string> > > > > argvec_t;
291 Lua.writeFunction("addSortList",
f3c18728 292 [&lci](const std::string& formask_,
3e61e7f7 293 const boost::variant<string, argvec_t>& masks,
294 boost::optional<int> order_)
295 {
296 try {
297 Netmask formask(formask_);
f3c18728 298 int order = order_ ? (*order_) : lci.sortlist.getMaxOrder(formask)+1;
3e61e7f7 299 if(auto str = boost::get<string>(&masks))
f3c18728 300 lci.sortlist.addEntry(formask, Netmask(*str), order);
3e61e7f7 301 else {
302
303 auto vec = boost::get<argvec_t>(&masks);
304 for(const auto& e : *vec) {
305 if(auto s = boost::get<string>(&e.second)) {
f3c18728 306 lci.sortlist.addEntry(formask, Netmask(*s), order);
3e61e7f7 307 }
308 else {
309 const auto& v =boost::get<vector<pair<int, string> > >(e.second);
dd079764
RG
310 for(const auto& entry : v)
311 lci.sortlist.addEntry(formask, Netmask(entry.second), order);
3e61e7f7 312 }
313 ++order;
314 }
315 }
316 }
317 catch(std::exception& e) {
e6a9dde5 318 g_log<<Logger::Error<<"Error in addSortList: "<<e.what()<<endl;
3e61e7f7 319 }
320 });
2ac8ae89 321
322 Lua.writeFunction("addDS", [&lci](const std::string& who, const std::string& what) {
e48c6b8a 323 warnIfDNSSECDisabled("Warning: adding Trust Anchor for DNSSEC (addDS), but dnssec is set to 'off'!");
52ad9eea 324 DNSName zone(who);
52ad9eea 325 auto ds = unique_ptr<DSRecordContent>(dynamic_cast<DSRecordContent*>(DSRecordContent::make(what)));
64a4a928 326 lci.dsAnchors[zone].insert(*ds);
52ad9eea 327 });
2ac8ae89 328
329 Lua.writeFunction("clearDS", [&lci](boost::optional<string> who) {
e48c6b8a 330 warnIfDNSSECDisabled("Warning: removing Trust Anchor for DNSSEC (clearDS), but dnssec is set to 'off'!");
2ac8ae89 331 if(who)
332 lci.dsAnchors.erase(DNSName(*who));
333 else
334 lci.dsAnchors.clear();
335 });
336
1bd49828 337 Lua.writeFunction("addNTA", [&lci](const std::string& who, const boost::optional<std::string> why) {
e48c6b8a 338 warnIfDNSSECDisabled("Warning: adding Negative Trust Anchor for DNSSEC (addNTA), but dnssec is set to 'off'!");
1bd49828
PL
339 if(why)
340 lci.negAnchors[DNSName(who)] = static_cast<string>(*why);
341 else
342 lci.negAnchors[DNSName(who)] = "";
343 });
344
345 Lua.writeFunction("clearNTA", [&lci](boost::optional<string> who) {
e48c6b8a 346 warnIfDNSSECDisabled("Warning: removing Negative Trust Anchor for DNSSEC (clearNTA), but dnssec is set to 'off'!");
1bd49828
PL
347 if(who)
348 lci.negAnchors.erase(DNSName(*who));
349 else
350 lci.negAnchors.clear();
351 });
352
aa7929a3 353#if HAVE_PROTOBUF
f1c7929a
RG
354 Lua.writeFunction("setProtobufMasks", [&lci](const uint8_t maskV4, uint8_t maskV6) {
355 lci.protobufMaskV4 = maskV4;
356 lci.protobufMaskV6 = maskV6;
357 });
358
359 Lua.writeFunction("protobufServer", [&lci](const string& server_, boost::optional<protobufOptions_t> vars) {
aa7929a3 360 try {
63341e8d 361 if (!lci.protobufExportConfig.enabled) {
63341e8d 362 lci.protobufExportConfig.enabled = true;
f1c7929a
RG
363 lci.protobufExportConfig.server = ComboAddress(server_);
364 parseProtobufOptions(vars, lci.protobufExportConfig);
aa7929a3
RG
365 }
366 else {
63341e8d 367 g_log<<Logger::Error<<"Only one protobuf server can be configured, we already have "<<lci.protobufExportConfig.server.toString()<<endl;
aa7929a3
RG
368 }
369 }
4898a348 370 catch(std::exception& e) {
e6a9dde5 371 g_log<<Logger::Error<<"Error while starting protobuf logger to '"<<server_<<": "<<e.what()<<endl;
4898a348
RG
372 }
373 catch(PDNSException& e) {
e6a9dde5 374 g_log<<Logger::Error<<"Error while starting protobuf logger to '"<<server_<<": "<<e.reason<<endl;
4898a348
RG
375 }
376 });
377
f1c7929a 378 Lua.writeFunction("outgoingProtobufServer", [&lci](const string& server_, boost::optional<protobufOptions_t> vars) {
4898a348 379 try {
63341e8d 380 if (!lci.outgoingProtobufExportConfig.enabled) {
63341e8d 381 lci.outgoingProtobufExportConfig.enabled = true;
f1c7929a
RG
382 lci.protobufExportConfig.server = ComboAddress(server_);
383 parseProtobufOptions(vars, lci.protobufExportConfig);
4898a348
RG
384 }
385 else {
63341e8d 386 g_log<<Logger::Error<<"Only one protobuf server can be configured, we already have "<<lci.outgoingProtobufExportConfig.server.toString()<<endl;
4898a348
RG
387 }
388 }
aa7929a3 389 catch(std::exception& e) {
e6a9dde5 390 g_log<<Logger::Error<<"Error while starting protobuf logger to '"<<server_<<": "<<e.what()<<endl;
aa7929a3
RG
391 }
392 catch(PDNSException& e) {
e6a9dde5 393 g_log<<Logger::Error<<"Error while starting protobuf logger to '"<<server_<<": "<<e.reason<<endl;
aa7929a3
RG
394 }
395 });
396#endif
397
ad42489c 398 try {
399 Lua.executeCode(ifs);
400 g_luaconfs.setState(lci);
401 }
2ac8ae89 402 catch(const LuaContext::ExecutionErrorException& e) {
e6a9dde5 403 g_log<<Logger::Error<<"Unable to load Lua script from '"+fname+"': ";
2ac8ae89 404 try {
405 std::rethrow_if_nested(e);
dd079764
RG
406 } catch(const std::exception& exp) {
407 // exp is the exception that was thrown from inside the lambda
e6a9dde5 408 g_log << exp.what() << std::endl;
2ac8ae89 409 }
dd079764
RG
410 catch(const PDNSException& exp) {
411 // exp is the exception that was thrown from inside the lambda
e6a9dde5 412 g_log << exp.reason << std::endl;
2ac8ae89 413 }
414 throw;
415
416 }
ad42489c 417 catch(std::exception& err) {
e6a9dde5 418 g_log<<Logger::Error<<"Unable to load Lua script from '"+fname+"': "<<err.what()<<endl;
2ac8ae89 419 throw;
ad42489c 420 }
421
3e61e7f7 422}
6a99b9a4 423
e6ec15bf
RG
424void startLuaConfigDelayedThreads(const luaConfigDelayedThreads& delayedThreads, uint64_t generation)
425{
426 for (const auto& rpzMaster : delayedThreads.rpzMasterThreads) {
427 try {
428 std::thread t(RPZIXFRTracker, std::get<0>(rpzMaster), std::get<1>(rpzMaster), std::get<2>(rpzMaster), std::get<3>(rpzMaster), std::get<4>(rpzMaster), std::get<5>(rpzMaster) * 1024 * 1024, std::get<6>(rpzMaster), std::get<7>(rpzMaster), std::get<8>(rpzMaster), std::get<9>(rpzMaster), generation);
429 t.detach();
430 }
431 catch(const std::exception& e) {
432 g_log<<Logger::Error<<"Problem starting RPZIXFRTracker thread: "<<e.what()<<endl;
433 exit(1); // FIXME proper exit code?
434 }
435 catch(const PDNSException& e) {
436 g_log<<Logger::Error<<"Problem starting RPZIXFRTracker thread: "<<e.reason<<endl;
437 exit(1); // FIXME proper exit code?
438 }
439 }
440}