]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/recursordist/rec-lua-conf.cc
rec: Update new b-root-server.net addresses in built-in hints.
[thirdparty/pdns.git] / pdns / recursordist / rec-lua-conf.cc
1 #include "config.h"
2 #include "ext/luawrapper/include/LuaContext.hpp"
3
4 #include <fstream>
5 #include <thread>
6 #include "namespaces.hh"
7 #include "logger.hh"
8 #include "lua-base4.hh"
9 #include "rec-lua-conf.hh"
10 #include "sortlist.hh"
11 #include "filterpo.hh"
12 #include "syncres.hh"
13 #include "rpzloader.hh"
14 #include "base64.hh"
15 #include "remote_logger.hh"
16 #include "validate.hh"
17 #include "validate-recursor.hh"
18 #include "root-dnssec.hh"
19
20 GlobalStateHolder<LuaConfigItems> g_luaconfs;
21
22 /* SO HOW DOES THIS WORK! AND PLEASE PAY ATTENTION!
23 This function can be called at any time. It is expected to overwrite all the contents
24 of LuaConfigItems, which is held in a GlobalStateHolder for RCU properties.
25
26 This function can be called again at a later date, so you must make sure that anything you
27 allow to be configured from here lives in g_luaconfs AND NOWHERE ELSE.
28
29 If someone loads an empty Lua file, the default LuaConfigItems struct MUST MAKE SENSE.
30
31 To make this easy on you, here is a LuaConfigItems constructor where you
32 can set sane defaults:
33 */
34
35 LuaConfigItems::LuaConfigItems()
36 {
37 DNSName root("."); // don't use g_rootdnsname here, it might not exist yet
38 for (const auto& dsRecord : rootDSs) {
39 auto ds = std::dynamic_pointer_cast<DSRecordContent>(DSRecordContent::make(dsRecord));
40 dsAnchors[root].insert(*ds);
41 }
42 }
43
44 /* DID YOU READ THE STORY ABOVE? */
45
46 bool operator==(const ProtobufExportConfig& configA, const ProtobufExportConfig& configB)
47 {
48 // clang-format off
49 return configA.exportTypes == configB.exportTypes &&
50 configA.servers == configB.servers &&
51 configA.maxQueuedEntries == configB.maxQueuedEntries &&
52 configA.timeout == configB.timeout &&
53 configA.reconnectWaitTime == configB.reconnectWaitTime &&
54 configA.asyncConnect == configB.asyncConnect &&
55 configA.enabled == configB.enabled &&
56 configA.logQueries == configB.logQueries &&
57 configA.logResponses == configB.logResponses &&
58 configA.taggedOnly == configB.taggedOnly &&
59 configA.logMappedFrom == configB.logMappedFrom;
60 // clang-format on
61 }
62
63 bool operator!=(const ProtobufExportConfig& configA, const ProtobufExportConfig& configB)
64 {
65 return !(configA == configB);
66 }
67
68 bool operator==(const FrameStreamExportConfig& configA, const FrameStreamExportConfig& configB)
69 {
70 // clang-format off
71 return configA.enabled == configB.enabled &&
72 configA.logQueries == configB.logQueries &&
73 configA.logResponses == configB.logResponses &&
74 configA.logNODs == configB.logNODs &&
75 configA.logUDRs == configB.logUDRs &&
76 configA.bufferHint == configB.bufferHint &&
77 configA.flushTimeout == configB.flushTimeout &&
78 configA.inputQueueSize == configB.inputQueueSize &&
79 configA.outputQueueSize == configB.outputQueueSize &&
80 configA.queueNotifyThreshold == configB.queueNotifyThreshold &&
81 configA.reopenInterval == configB.reopenInterval &&
82 configA.servers == configB.servers;
83 // clang-format on
84 }
85
86 bool operator!=(const FrameStreamExportConfig& configA, const FrameStreamExportConfig& configB)
87 {
88 return !(configA == configB);
89 }
90
91 template <typename C>
92 typename C::value_type::second_type constGet(const C& c, const std::string& name)
93 {
94 auto iter = c.find(name);
95 if (iter == c.end())
96 return 0;
97 return iter->second;
98 }
99
100 typedef std::unordered_map<std::string, boost::variant<bool, uint32_t, std::string, std::vector<std::pair<int, std::string>>>> rpzOptions_t;
101
102 static void parseRPZParameters(rpzOptions_t& have, std::shared_ptr<DNSFilterEngine::Zone>& zone, std::string& polName, boost::optional<DNSFilterEngine::Policy>& defpol, bool& defpolOverrideLocal, uint32_t& maxTTL)
103 {
104 if (have.count("policyName") != 0) {
105 polName = boost::get<std::string>(have["policyName"]);
106 }
107 if (have.count("defpol") != 0) {
108 defpol = DNSFilterEngine::Policy();
109 defpol->d_kind = (DNSFilterEngine::PolicyKind)boost::get<uint32_t>(have["defpol"]);
110 defpol->setName(polName);
111 if (defpol->d_kind == DNSFilterEngine::PolicyKind::Custom) {
112 defpol->d_custom.push_back(DNSRecordContent::mastermake(QType::CNAME, QClass::IN,
113 boost::get<string>(have["defcontent"])));
114
115 if (have.count("defttl") != 0) {
116 defpol->d_ttl = static_cast<int32_t>(boost::get<uint32_t>(have["defttl"]));
117 }
118 else {
119 defpol->d_ttl = -1; // get it from the zone
120 }
121 }
122
123 if (have.count("defpolOverrideLocalData") != 0) {
124 defpolOverrideLocal = boost::get<bool>(have["defpolOverrideLocalData"]);
125 }
126 }
127 if (have.count("maxTTL") != 0) {
128 maxTTL = boost::get<uint32_t>(have["maxTTL"]);
129 }
130 if (have.count("zoneSizeHint") != 0) {
131 auto zoneSizeHint = static_cast<size_t>(boost::get<uint32_t>(have["zoneSizeHint"]));
132 if (zoneSizeHint > 0) {
133 zone->reserve(zoneSizeHint);
134 }
135 }
136 if (have.count("tags") != 0) {
137 const auto tagsTable = boost::get<std::vector<std::pair<int, std::string>>>(have["tags"]);
138 std::unordered_set<std::string> tags;
139 for (const auto& tag : tagsTable) {
140 tags.insert(tag.second);
141 }
142 zone->setTags(std::move(tags));
143 }
144 if (have.count("overridesGettag") != 0) {
145 zone->setPolicyOverridesGettag(boost::get<bool>(have["overridesGettag"]));
146 }
147 if (have.count("extendedErrorCode") != 0) {
148 auto code = boost::get<uint32_t>(have["extendedErrorCode"]);
149 if (code > std::numeric_limits<uint16_t>::max()) {
150 throw std::runtime_error("Invalid extendedErrorCode value " + std::to_string(code) + " in RPZ configuration");
151 }
152
153 zone->setExtendedErrorCode(static_cast<uint16_t>(code));
154 if (have.count("extendedErrorExtra") != 0) {
155 zone->setExtendedErrorExtra(boost::get<std::string>(have["extendedErrorExtra"]));
156 }
157 }
158 if (have.count("includeSOA") != 0) {
159 zone->setIncludeSOA(boost::get<bool>(have["includeSOA"]));
160 }
161 }
162
163 typedef std::unordered_map<std::string, boost::variant<bool, uint64_t, std::string, std::vector<std::pair<int, std::string>>>> protobufOptions_t;
164
165 static void parseProtobufOptions(boost::optional<protobufOptions_t> vars, ProtobufExportConfig& config)
166 {
167 if (!vars) {
168 return;
169 }
170
171 if (vars->count("timeout")) {
172 config.timeout = boost::get<uint64_t>((*vars)["timeout"]);
173 }
174
175 if (vars->count("maxQueuedEntries")) {
176 config.maxQueuedEntries = boost::get<uint64_t>((*vars)["maxQueuedEntries"]);
177 }
178
179 if (vars->count("reconnectWaitTime")) {
180 config.reconnectWaitTime = boost::get<uint64_t>((*vars)["reconnectWaitTime"]);
181 }
182
183 if (vars->count("asyncConnect")) {
184 config.asyncConnect = boost::get<bool>((*vars)["asyncConnect"]);
185 }
186
187 if (vars->count("taggedOnly")) {
188 config.taggedOnly = boost::get<bool>((*vars)["taggedOnly"]);
189 }
190
191 if (vars->count("logQueries")) {
192 config.logQueries = boost::get<bool>((*vars)["logQueries"]);
193 }
194
195 if (vars->count("logResponses")) {
196 config.logResponses = boost::get<bool>((*vars)["logResponses"]);
197 }
198
199 if (vars->count("logMappedFrom")) {
200 config.logMappedFrom = boost::get<bool>((*vars)["logMappedFrom"]);
201 }
202
203 if (vars->count("exportTypes")) {
204 config.exportTypes.clear();
205
206 auto types = boost::get<std::vector<std::pair<int, std::string>>>((*vars)["exportTypes"]);
207 for (const auto& pair : types) {
208 const auto& type = pair.second;
209
210 QType qtype;
211 try {
212 qtype = std::stoul(type);
213 }
214 catch (const std::exception& ex) {
215 qtype = QType::chartocode(type.c_str());
216 if (qtype == 0) {
217 throw std::runtime_error("Unknown QType '" + type + "' in protobuf's export types");
218 }
219 }
220 config.exportTypes.insert(qtype);
221 }
222 }
223 }
224
225 #ifdef HAVE_FSTRM
226 typedef std::unordered_map<std::string, boost::variant<bool, uint64_t, std::string, std::vector<std::pair<int, std::string>>>> frameStreamOptions_t;
227
228 static void parseFrameStreamOptions(boost::optional<frameStreamOptions_t> vars, FrameStreamExportConfig& config)
229 {
230 if (!vars) {
231 return;
232 }
233
234 if (vars->count("logQueries")) {
235 config.logQueries = boost::get<bool>((*vars)["logQueries"]);
236 }
237 if (vars->count("logResponses")) {
238 config.logResponses = boost::get<bool>((*vars)["logResponses"]);
239 }
240 if (vars->count("logNODs")) {
241 config.logNODs = boost::get<bool>((*vars)["logNODs"]);
242 }
243 if (vars->count("logUDRs")) {
244 config.logUDRs = boost::get<bool>((*vars)["logUDRs"]);
245 }
246
247 if (vars->count("bufferHint")) {
248 config.bufferHint = boost::get<uint64_t>((*vars)["bufferHint"]);
249 }
250 if (vars->count("flushTimeout")) {
251 config.flushTimeout = boost::get<uint64_t>((*vars)["flushTimeout"]);
252 }
253 if (vars->count("inputQueueSize")) {
254 config.inputQueueSize = boost::get<uint64_t>((*vars)["inputQueueSize"]);
255 }
256 if (vars->count("outputQueueSize")) {
257 config.outputQueueSize = boost::get<uint64_t>((*vars)["outputQueueSize"]);
258 }
259 if (vars->count("queueNotifyThreshold")) {
260 config.queueNotifyThreshold = boost::get<uint64_t>((*vars)["queueNotifyThreshold"]);
261 }
262 if (vars->count("reopenInterval")) {
263 config.reopenInterval = boost::get<uint64_t>((*vars)["reopenInterval"]);
264 }
265 }
266 #endif /* HAVE_FSTRM */
267
268 static void rpzPrimary(LuaConfigItems& lci, luaConfigDelayedThreads& delayedThreads, const boost::variant<string, std::vector<std::pair<int, string>>>& primaries_, const string& zoneName, boost::optional<rpzOptions_t> options)
269 {
270 boost::optional<DNSFilterEngine::Policy> defpol;
271 bool defpolOverrideLocal = true;
272 std::shared_ptr<DNSFilterEngine::Zone> zone = std::make_shared<DNSFilterEngine::Zone>();
273 TSIGTriplet tt;
274 uint32_t refresh = 0;
275 size_t maxReceivedXFRMBytes = 0;
276 uint16_t axfrTimeout = 20;
277 uint32_t maxTTL = std::numeric_limits<uint32_t>::max();
278 ComboAddress localAddress;
279 std::vector<ComboAddress> primaries;
280 if (primaries_.type() == typeid(string)) {
281 primaries.push_back(ComboAddress(boost::get<std::string>(primaries_), 53));
282 }
283 else {
284 for (const auto& primary : boost::get<std::vector<std::pair<int, std::string>>>(primaries_)) {
285 primaries.push_back(ComboAddress(primary.second, 53));
286 }
287 }
288
289 size_t zoneIdx;
290 std::string dumpFile;
291 std::shared_ptr<const SOARecordContent> sr = nullptr;
292
293 try {
294 std::string seedFile;
295 std::string polName(zoneName);
296
297 if (options) {
298 auto& have = *options;
299 parseRPZParameters(have, zone, polName, defpol, defpolOverrideLocal, maxTTL);
300
301 if (have.count("tsigname")) {
302 tt.name = DNSName(toLower(boost::get<string>(have["tsigname"])));
303 tt.algo = DNSName(toLower(boost::get<string>(have["tsigalgo"])));
304 if (B64Decode(boost::get<string>(have["tsigsecret"]), tt.secret))
305 throw std::runtime_error("TSIG secret is not valid Base-64 encoded");
306 }
307
308 if (have.count("refresh")) {
309 refresh = boost::get<uint32_t>(have["refresh"]);
310 if (refresh == 0) {
311 SLOG(g_log << Logger::Warning << "rpzPrimary refresh value of 0 ignored" << endl,
312 lci.d_slog->info(Logr::Warning, "rpzPrimary refresh value of 0 ignored"));
313 }
314 }
315
316 if (have.count("maxReceivedMBytes")) {
317 maxReceivedXFRMBytes = static_cast<size_t>(boost::get<uint32_t>(have["maxReceivedMBytes"]));
318 }
319
320 if (have.count("localAddress")) {
321 localAddress = ComboAddress(boost::get<string>(have["localAddress"]));
322 }
323
324 if (have.count("axfrTimeout")) {
325 axfrTimeout = static_cast<uint16_t>(boost::get<uint32_t>(have["axfrTimeout"]));
326 }
327
328 if (have.count("seedFile")) {
329 seedFile = boost::get<std::string>(have["seedFile"]);
330 }
331
332 if (have.count("dumpFile")) {
333 dumpFile = boost::get<std::string>(have["dumpFile"]);
334 }
335 }
336
337 if (localAddress != ComboAddress()) {
338 // We were passed a localAddress, check if its AF matches the primaries'
339 for (const auto& primary : primaries) {
340 if (localAddress.sin4.sin_family != primary.sin4.sin_family) {
341 throw PDNSException("Primary address(" + primary.toString() + ") is not of the same Address Family as the local address (" + localAddress.toString() + ").");
342 }
343 }
344 }
345
346 DNSName domain(zoneName);
347 zone->setDomain(domain);
348 zone->setName(polName);
349 zoneIdx = lci.dfe.addZone(zone);
350
351 auto log = lci.d_slog->withValues("seedfile", Logging::Loggable(seedFile), "zone", Logging::Loggable(zoneName));
352 if (!seedFile.empty()) {
353 SLOG(g_log << Logger::Info << "Pre-loading RPZ zone " << zoneName << " from seed file '" << seedFile << "'" << endl,
354 log->info(Logr::Info, "Pre-loading RPZ zone from seed file"));
355 try {
356 sr = loadRPZFromFile(seedFile, zone, defpol, defpolOverrideLocal, maxTTL);
357
358 if (zone->getDomain() != domain) {
359 throw PDNSException("The RPZ zone " + zoneName + " loaded from the seed file (" + zone->getDomain().toString() + ") does not match the one passed in parameter (" + domain.toString() + ")");
360 }
361
362 if (sr == nullptr) {
363 throw PDNSException("The RPZ zone " + zoneName + " loaded from the seed file (" + zone->getDomain().toString() + ") has no SOA record");
364 }
365 }
366 catch (const PDNSException& e) {
367 SLOG(g_log << Logger::Warning << "Unable to pre-load RPZ zone " << zoneName << " from seed file '" << seedFile << "': " << e.reason << endl,
368 log->error(Logr::Warning, e.reason, "Exception while pre-loading RPZ zone", "exception", Logging::Loggable("PDNSException")));
369 zone->clear();
370 }
371 catch (const std::exception& e) {
372 SLOG(g_log << Logger::Warning << "Unable to pre-load RPZ zone " << zoneName << " from seed file '" << seedFile << "': " << e.what() << endl,
373 log->error(Logr::Warning, e.what(), "Exception while pre-loading RPZ zone", "exception", Logging::Loggable("std::exception")));
374 zone->clear();
375 }
376 }
377 }
378 catch (const std::exception& e) {
379 SLOG(g_log << Logger::Error << "Problem configuring 'rpzPrimary': " << e.what() << endl,
380 lci.d_slog->error(Logr::Error, e.what(), "Exception configuring 'rpzPrimary'", "exception", Logging::Loggable("std::exception")));
381 }
382 catch (const PDNSException& e) {
383 SLOG(g_log << Logger::Error << "Problem configuring 'rpzPrimary': " << e.reason << endl,
384 lci.d_slog->error(Logr::Error, e.reason, "Exception configuring 'rpzPrimary'", Logging::Loggable("PDNSException")));
385 }
386
387 delayedThreads.rpzPrimaryThreads.emplace_back(primaries, defpol, defpolOverrideLocal, maxTTL, zoneIdx, tt, maxReceivedXFRMBytes, localAddress, axfrTimeout, refresh, sr, dumpFile);
388 }
389
390 // A wrapper class that loads the standard Lua defintions into the context, so that we can use things like pdns.A
391 class RecLuaConfigContext : public BaseLua4
392 {
393 public:
394 RecLuaConfigContext()
395 {
396 prepareContext();
397 }
398 void postPrepareContext() override
399 {
400 // clang-format off
401 d_pd.push_back({"AdditionalMode", in_t{
402 {"Ignore", static_cast<int>(AdditionalMode::Ignore)},
403 {"CacheOnly", static_cast<int>(AdditionalMode::CacheOnly)},
404 {"CacheOnlyRequireAuth", static_cast<int>(AdditionalMode::CacheOnlyRequireAuth)},
405 {"ResolveImmediately", static_cast<int>(AdditionalMode::ResolveImmediately)},
406 {"ResolveDeferred", static_cast<int>(AdditionalMode::ResolveDeferred)}
407 }});
408 }
409 void postLoad() override
410 {
411 }
412 LuaContext* operator->()
413 {
414 return d_lw.get();
415 }
416 };
417
418 void loadRecursorLuaConfig(const std::string& fname, luaConfigDelayedThreads& delayedThreads, ProxyMapping& proxyMapping)
419 {
420 LuaConfigItems lci;
421 lci.d_slog = g_slog->withName("luaconfig");
422
423 RecLuaConfigContext Lua;
424
425 if (fname.empty())
426 return;
427 ifstream ifs(fname);
428 if (!ifs)
429 throw PDNSException("Cannot open file '" + fname + "': " + stringerror());
430
431 auto luaconfsLocal = g_luaconfs.getLocal();
432 lci.generation = luaconfsLocal->generation + 1;
433
434 Lua->writeFunction("clearSortlist", [&lci]() { lci.sortlist.clear(); });
435
436 /* we can get: "1.2.3.4"
437 {"1.2.3.4", "4.5.6.7"}
438 {"1.2.3.4", {"4.5.6.7", "8.9.10.11"}}
439 */
440
441 map<string, DNSFilterEngine::PolicyKind> pmap{
442 {"NoAction", DNSFilterEngine::PolicyKind::NoAction},
443 {"Drop", DNSFilterEngine::PolicyKind::Drop},
444 {"NXDOMAIN", DNSFilterEngine::PolicyKind::NXDOMAIN},
445 {"NODATA", DNSFilterEngine::PolicyKind::NODATA},
446 {"Truncate", DNSFilterEngine::PolicyKind::Truncate},
447 {"Custom", DNSFilterEngine::PolicyKind::Custom}};
448 Lua->writeVariable("Policy", pmap);
449
450 Lua->writeFunction("rpzFile", [&lci](const string& filename, boost::optional<rpzOptions_t> options) {
451 auto log = lci.d_slog->withValues("file", Logging::Loggable(filename));
452 try {
453 boost::optional<DNSFilterEngine::Policy> defpol;
454 bool defpolOverrideLocal = true;
455 std::string polName("rpzFile");
456 std::shared_ptr<DNSFilterEngine::Zone> zone = std::make_shared<DNSFilterEngine::Zone>();
457 uint32_t maxTTL = std::numeric_limits<uint32_t>::max();
458 if (options) {
459 auto& have = *options;
460 parseRPZParameters(have, zone, polName, defpol, defpolOverrideLocal, maxTTL);
461 }
462 SLOG(g_log << Logger::Warning << "Loading RPZ from file '" << filename << "'" << endl,
463 log->info(Logr::Info, "Loading RPZ from file"));
464 zone->setName(polName);
465 loadRPZFromFile(filename, zone, defpol, defpolOverrideLocal, maxTTL);
466 lci.dfe.addZone(zone);
467 SLOG(g_log << Logger::Warning << "Done loading RPZ from file '" << filename << "'" << endl,
468 log->info(Logr::Info, "Done loading RPZ from file"));
469 }
470 catch (const std::exception& e) {
471 SLOG(g_log << Logger::Error << "Unable to load RPZ zone from '" << filename << "': " << e.what() << endl,
472 log->error(Logr::Error, e.what(), "Exception while loading RPZ zone from file"));
473 }
474 });
475
476 Lua->writeFunction("rpzMaster", [&lci, &delayedThreads](const boost::variant<string, std::vector<std::pair<int, string>>>& primaries_, const string& zoneName, boost::optional<rpzOptions_t> options) {
477 SLOG(g_log << Logger::Warning << "'rpzMaster' is deprecated and will be removed in a future release, use 'rpzPrimary' instead" << endl,
478 lci.d_slog->info(Logr::Warning, "'rpzMaster' is deprecated and will be removed in a future release, use 'rpzPrimary' instead"));
479 rpzPrimary(lci, delayedThreads, primaries_, zoneName, options);
480 });
481 Lua->writeFunction("rpzPrimary", [&lci, &delayedThreads](const boost::variant<string, std::vector<std::pair<int, string>>>& primaries_, const string& zoneName, boost::optional<rpzOptions_t> options) {
482 rpzPrimary(lci, delayedThreads, primaries_, zoneName, options);
483 });
484
485 typedef std::unordered_map<std::string, boost::variant<uint32_t, std::string>> zoneToCacheOptions_t;
486
487 Lua->writeFunction("zoneToCache", [&lci](const string& zoneName, const string& method, const boost::variant<string, std::vector<std::pair<int, string>>>& srcs, boost::optional<zoneToCacheOptions_t> options) {
488 try {
489 RecZoneToCache::Config conf;
490 DNSName validZoneName(zoneName);
491 conf.d_zone = zoneName;
492 const set<string> methods = {"axfr", "url", "file"};
493 if (methods.count(method) == 0) {
494 throw std::runtime_error("unknwon method '" + method + "'");
495 }
496 conf.d_method = method;
497 if (srcs.type() == typeid(std::string)) {
498 conf.d_sources.push_back(boost::get<std::string>(srcs));
499 }
500 else {
501 for (const auto& src : boost::get<std::vector<std::pair<int, std::string>>>(srcs)) {
502 conf.d_sources.push_back(src.second);
503 }
504 }
505 if (conf.d_sources.size() == 0) {
506 throw std::runtime_error("at least one source required");
507 }
508 if (options) {
509 auto& have = *options;
510 if (have.count("timeout")) {
511 conf.d_timeout = boost::get<uint32_t>(have.at("timeout"));
512 }
513 if (have.count("tsigname")) {
514 conf.d_tt.name = DNSName(toLower(boost::get<string>(have.at("tsigname"))));
515 conf.d_tt.algo = DNSName(toLower(boost::get<string>(have.at("tsigalgo"))));
516 if (B64Decode(boost::get<string>(have.at("tsigsecret")), conf.d_tt.secret)) {
517 throw std::runtime_error("TSIG secret is not valid Base-64 encoded");
518 }
519 }
520 if (have.count("maxReceivedMBytes")) {
521 conf.d_maxReceivedBytes = static_cast<size_t>(boost::get<uint32_t>(have.at("maxReceivedMBytes")));
522 conf.d_maxReceivedBytes *= 1024 * 1024;
523 }
524 if (have.count("localAddress")) {
525 conf.d_local = ComboAddress(boost::get<string>(have.at("localAddress")));
526 }
527 if (have.count("refreshPeriod")) {
528 conf.d_refreshPeriod = boost::get<uint32_t>(have.at("refreshPeriod"));
529 }
530 if (have.count("retryOnErrorPeriod")) {
531 conf.d_retryOnError = boost::get<uint32_t>(have.at("retryOnErrorPeriod"));
532 }
533 const map<string, pdns::ZoneMD::Config> nameToVal = {
534 {"ignore", pdns::ZoneMD::Config::Ignore},
535 {"validate", pdns::ZoneMD::Config::Validate},
536 {"require", pdns::ZoneMD::Config::Require},
537 };
538 if (have.count("zonemd")) {
539 string zonemdValidation = boost::get<string>(have.at("zonemd"));
540 auto it = nameToVal.find(zonemdValidation);
541 if (it == nameToVal.end()) {
542 throw std::runtime_error(zonemdValidation + " is not a valid value for `zonemd`");
543 }
544 else {
545 conf.d_zonemd = it->second;
546 }
547 }
548 if (have.count("dnssec")) {
549 string dnssec = boost::get<string>(have.at("dnssec"));
550 auto it = nameToVal.find(dnssec);
551 if (it == nameToVal.end()) {
552 throw std::runtime_error(dnssec + " is not a valid value for `dnssec`");
553 }
554 else {
555 conf.d_dnssec = it->second;
556 }
557 }
558 }
559
560 lci.ztcConfigs[validZoneName] = conf;
561 }
562 catch (const std::exception& e) {
563 SLOG(g_log << Logger::Error << "Problem configuring zoneToCache for zone '" << zoneName << "': " << e.what() << endl,
564 lci.d_slog->error(Logr::Error, e.what(), "Problem configuring zoneToCache", "zone", Logging::Loggable(zoneName),
565 "exception", Logging::Loggable("std::exception")));
566 }
567 });
568
569 typedef vector<pair<int, boost::variant<string, vector<pair<int, string>>>>> argvec_t;
570 Lua->writeFunction("addSortList",
571 [&lci](const std::string& formask_,
572 const boost::variant<string, argvec_t>& masks,
573 boost::optional<int> order_) {
574 try {
575 Netmask formask(formask_);
576 int order = order_ ? (*order_) : lci.sortlist.getMaxOrder(formask) + 1;
577 if (auto str = boost::get<string>(&masks))
578 lci.sortlist.addEntry(formask, Netmask(*str), order);
579 else {
580
581 auto vec = boost::get<argvec_t>(&masks);
582 for (const auto& e : *vec) {
583 if (auto s = boost::get<string>(&e.second)) {
584 lci.sortlist.addEntry(formask, Netmask(*s), order);
585 }
586 else {
587 const auto& v = boost::get<vector<pair<int, string>>>(e.second);
588 for (const auto& entry : v)
589 lci.sortlist.addEntry(formask, Netmask(entry.second), order);
590 }
591 ++order;
592 }
593 }
594 }
595 catch (std::exception& e) {
596 SLOG(g_log << Logger::Error << "Error in addSortList: " << e.what() << endl,
597 lci.d_slog->error(Logr::Error, e.what(), "Error in addSortList", "exception", Logging::Loggable("std::exception")));
598 }
599 });
600
601 Lua->writeFunction("addTA", [&lci](const std::string& who, const std::string& what) {
602 warnIfDNSSECDisabled("Warning: adding Trust Anchor for DNSSEC (addTA), but dnssec is set to 'off'!");
603 DNSName zone(who);
604 auto ds = std::dynamic_pointer_cast<DSRecordContent>(DSRecordContent::make(what));
605 lci.dsAnchors[zone].insert(*ds);
606 });
607
608 Lua->writeFunction("clearTA", [&lci](boost::optional<string> who) {
609 warnIfDNSSECDisabled("Warning: removing Trust Anchor for DNSSEC (clearTA), but dnssec is set to 'off'!");
610 if (who)
611 lci.dsAnchors.erase(DNSName(*who));
612 else
613 lci.dsAnchors.clear();
614 });
615
616 /* Remove in 4.3 */
617 Lua->writeFunction("addDS", [&lci](const std::string& who, const std::string& what) {
618 warnIfDNSSECDisabled("Warning: adding Trust Anchor for DNSSEC (addDS), but dnssec is set to 'off'!");
619 SLOG(g_log << Logger::Warning << "addDS is deprecated and will be removed in the future, switch to addTA" << endl,
620 lci.d_slog->info(Logr::Warning, "addDS is deprecated and will be removed in the future, switch to addTA"));
621 DNSName zone(who);
622 auto ds = std::dynamic_pointer_cast<DSRecordContent>(DSRecordContent::make(what));
623 lci.dsAnchors[zone].insert(*ds);
624 });
625
626 /* Remove in 4.3 */
627 Lua->writeFunction("clearDS", [&lci](boost::optional<string> who) {
628 SLOG(g_log << Logger::Warning << "clearDS is deprecated and will be removed in the future, switch to clearTA" << endl,
629 lci.d_slog->info(Logr::Warning, "clearDS is deprecated and will be removed in the future, switch to clearTA"));
630 warnIfDNSSECDisabled("Warning: removing Trust Anchor for DNSSEC (clearDS), but dnssec is set to 'off'!");
631 if (who)
632 lci.dsAnchors.erase(DNSName(*who));
633 else
634 lci.dsAnchors.clear();
635 });
636
637 Lua->writeFunction("addNTA", [&lci](const std::string& who, const boost::optional<std::string> why) {
638 warnIfDNSSECDisabled("Warning: adding Negative Trust Anchor for DNSSEC (addNTA), but dnssec is set to 'off'!");
639 if (why)
640 lci.negAnchors[DNSName(who)] = static_cast<string>(*why);
641 else
642 lci.negAnchors[DNSName(who)] = "";
643 });
644
645 Lua->writeFunction("clearNTA", [&lci](boost::optional<string> who) {
646 warnIfDNSSECDisabled("Warning: removing Negative Trust Anchor for DNSSEC (clearNTA), but dnssec is set to 'off'!");
647 if (who)
648 lci.negAnchors.erase(DNSName(*who));
649 else
650 lci.negAnchors.clear();
651 });
652
653 Lua->writeFunction("readTrustAnchorsFromFile", [&lci](const std::string& fnamearg, const boost::optional<uint32_t> interval) {
654 uint32_t realInterval = 24;
655 if (interval) {
656 realInterval = static_cast<uint32_t>(*interval);
657 }
658 warnIfDNSSECDisabled("Warning: reading Trust Anchors from file (readTrustAnchorsFromFile), but dnssec is set to 'off'!");
659 lci.trustAnchorFileInfo.fname = fnamearg;
660 lci.trustAnchorFileInfo.interval = realInterval;
661 updateTrustAnchorsFromFile(fnamearg, lci.dsAnchors, lci.d_slog);
662 });
663
664 Lua->writeFunction("setProtobufMasks", [&lci](const uint8_t maskV4, uint8_t maskV6) {
665 lci.protobufMaskV4 = maskV4;
666 lci.protobufMaskV6 = maskV6;
667 });
668
669 Lua->writeFunction("protobufServer", [&lci](boost::variant<const std::string, const std::unordered_map<int, std::string>> servers, boost::optional<protobufOptions_t> vars) {
670 if (!lci.protobufExportConfig.enabled) {
671
672 lci.protobufExportConfig.enabled = true;
673
674 try {
675 if (servers.type() == typeid(std::string)) {
676 auto server = boost::get<const std::string>(servers);
677
678 lci.protobufExportConfig.servers.emplace_back(server);
679 }
680 else {
681 auto serversMap = boost::get<const std::unordered_map<int, std::string>>(servers);
682 for (const auto& serverPair : serversMap) {
683 lci.protobufExportConfig.servers.emplace_back(serverPair.second);
684 }
685 }
686
687 parseProtobufOptions(vars, lci.protobufExportConfig);
688 }
689 catch (std::exception& e) {
690 SLOG(g_log << Logger::Error << "Error while adding protobuf logger: " << e.what() << endl,
691 lci.d_slog->error(Logr::Error, e.what(), "Exception while adding protobuf logger", "exception", Logging::Loggable("std::exception")));
692 }
693 catch (PDNSException& e) {
694 SLOG(g_log << Logger::Error << "Error while adding protobuf logger: " << e.reason << endl,
695 lci.d_slog->error(Logr::Error, e.reason, "Exception while adding protobuf logger", "exception", Logging::Loggable("PDNSException")));
696 }
697 }
698 else {
699 SLOG(g_log << Logger::Error << "Only one protobufServer() directive can be configured, we already have " << lci.protobufExportConfig.servers.at(0).toString() << endl,
700 lci.d_slog->info(Logr::Error, "Only one protobufServer() directive can be configured", "existing", Logging::Loggable(lci.protobufExportConfig.servers.at(0).toString())));
701 }
702 });
703
704 Lua->writeFunction("outgoingProtobufServer", [&lci](boost::variant<const std::string, const std::unordered_map<int, std::string>> servers, boost::optional<protobufOptions_t> vars) {
705 if (!lci.outgoingProtobufExportConfig.enabled) {
706
707 lci.outgoingProtobufExportConfig.enabled = true;
708
709 try {
710 if (servers.type() == typeid(std::string)) {
711 auto server = boost::get<const std::string>(servers);
712
713 lci.outgoingProtobufExportConfig.servers.emplace_back(server);
714 }
715 else {
716 auto serversMap = boost::get<const std::unordered_map<int, std::string>>(servers);
717 for (const auto& serverPair : serversMap) {
718 lci.outgoingProtobufExportConfig.servers.emplace_back(serverPair.second);
719 }
720 }
721
722 parseProtobufOptions(vars, lci.outgoingProtobufExportConfig);
723 }
724 catch (std::exception& e) {
725 SLOG(g_log << Logger::Error << "Error while starting outgoing protobuf logger: " << e.what() << endl,
726 lci.d_slog->error(Logr::Error, "Exception while starting outgoing protobuf logger", "exception", Logging::Loggable("std::exception")));
727 }
728 catch (PDNSException& e) {
729 SLOG(g_log << Logger::Error << "Error while starting outgoing protobuf logger: " << e.reason << endl,
730 lci.d_slog->error(Logr::Error, "Exception while starting outgoing protobuf logger", "exception", Logging::Loggable("PDNSException")));
731 }
732 }
733 else {
734 SLOG(g_log << Logger::Error << "Only one outgoingProtobufServer() directive can be configured, we already have " << lci.outgoingProtobufExportConfig.servers.at(0).toString() << endl,
735 lci.d_slog->info(Logr::Error, "Only one outgoingProtobufServer() directive can be configured", "existing", Logging::Loggable(lci.outgoingProtobufExportConfig.servers.at(0).toString())));
736 }
737 });
738
739 #ifdef HAVE_FSTRM
740 Lua->writeFunction("dnstapFrameStreamServer", [&lci](boost::variant<const std::string, const std::unordered_map<int, std::string>> servers, boost::optional<frameStreamOptions_t> vars) {
741 if (!lci.frameStreamExportConfig.enabled) {
742
743 lci.frameStreamExportConfig.enabled = true;
744
745 try {
746 if (servers.type() == typeid(std::string)) {
747 auto server = boost::get<const std::string>(servers);
748 if (!boost::starts_with(server, "/")) {
749 ComboAddress parsecheck(server);
750 }
751 lci.frameStreamExportConfig.servers.emplace_back(server);
752 }
753 else {
754 auto serversMap = boost::get<const std::unordered_map<int, std::string>>(servers);
755 for (const auto& serverPair : serversMap) {
756 lci.frameStreamExportConfig.servers.emplace_back(serverPair.second);
757 }
758 }
759
760 parseFrameStreamOptions(vars, lci.frameStreamExportConfig);
761 }
762 catch (std::exception& e) {
763 SLOG(g_log << Logger::Error << "Error reading config for dnstap framestream logger: " << e.what() << endl,
764 lci.d_slog->error(Logr::Error, "Exception reading config for dnstap framestream logger", "exception", Logging::Loggable("std::exception")));
765 }
766 catch (PDNSException& e) {
767 SLOG(g_log << Logger::Error << "Error reading config for dnstap framestream logger: " << e.reason << endl,
768 lci.d_slog->error(Logr::Error, "Exception reading config for dnstap framestream logger", "exception", Logging::Loggable("PDNSException")));
769 }
770 }
771 else {
772 SLOG(g_log << Logger::Error << "Only one dnstapFrameStreamServer() directive can be configured, we already have " << lci.frameStreamExportConfig.servers.at(0) << endl,
773 lci.d_slog->info(Logr::Error, "Only one dnstapFrameStreamServer() directive can be configured", "existing", Logging::Loggable(lci.frameStreamExportConfig.servers.at(0))));
774 }
775 });
776 Lua->writeFunction("dnstapNODFrameStreamServer", [&lci](boost::variant<const std::string, const std::unordered_map<int, std::string>> servers, boost::optional<frameStreamOptions_t> vars) {
777 if (!lci.nodFrameStreamExportConfig.enabled) {
778 lci.nodFrameStreamExportConfig.enabled = true;
779
780 try {
781 if (servers.type() == typeid(std::string)) {
782 auto server = boost::get<const std::string>(servers);
783 if (!boost::starts_with(server, "/")) {
784 ComboAddress parsecheck(server);
785 }
786 lci.nodFrameStreamExportConfig.servers.emplace_back(server);
787 }
788 else {
789 auto serversMap = boost::get<const std::unordered_map<int, std::string>>(servers);
790 for (const auto& serverPair : serversMap) {
791 lci.nodFrameStreamExportConfig.servers.emplace_back(serverPair.second);
792 }
793 }
794
795 parseFrameStreamOptions(vars, lci.nodFrameStreamExportConfig);
796 }
797 catch (std::exception& e) {
798 SLOG(g_log << Logger::Error << "Error reading config for dnstap NOD framestream logger: " << e.what() << endl,
799 lci.d_slog->error(Logr::Error, "Exception reading config for dnstap NOD framestream logger", "exception", Logging::Loggable("std::exception")));
800 }
801 catch (PDNSException& e) {
802 SLOG(g_log << Logger::Error << "Error reading config for dnstap NOD framestream logger: " << e.reason << endl,
803 lci.d_slog->error(Logr::Error, "Exception reading config for dnstap NOD framestream logger", "exception", Logging::Loggable("PDNSException")));
804 }
805 }
806 else {
807 SLOG(g_log << Logger::Error << "Only one dnstapNODFrameStreamServer() directive can be configured, we already have " << lci.nodFrameStreamExportConfig.servers.at(0) << endl,
808 lci.d_slog->info(Logr::Error, "Only one dnstapNODFrameStreamServer() directive can be configured", "existing", Logging::Loggable(lci.nodFrameStreamExportConfig.servers.at(0))));
809 }
810 });
811 #endif /* HAVE_FSTRM */
812
813 Lua->writeFunction("addAllowedAdditionalQType", [&lci](int qtype, std::unordered_map<int, int> targetqtypes, boost::optional<std::map<std::string, int>> options) {
814 switch (qtype) {
815 case QType::MX:
816 case QType::SRV:
817 case QType::SVCB:
818 case QType::HTTPS:
819 case QType::NAPTR:
820 break;
821 default:
822 SLOG(g_log << Logger::Error << "addAllowedAdditionalQType does not support " << QType(qtype).toString() << endl,
823 lci.d_slog->info(Logr::Error, "addAllowedAdditionalQType does not support this qtype", "qtype", Logging::Loggable(QType(qtype).toString())));
824 return;
825 }
826
827 std::set<QType> targets;
828 for (const auto& t : targetqtypes) {
829 targets.emplace(QType(t.second));
830 }
831
832 AdditionalMode mode = AdditionalMode::CacheOnlyRequireAuth; // Always cheap and should be safe
833
834 if (options) {
835 if (const auto it = options->find("mode"); it != options->end()) {
836 mode = static_cast<AdditionalMode>(it->second);
837 if (mode > AdditionalMode::ResolveDeferred) {
838 SLOG(g_log << Logger::Error << "addAllowedAdditionalQType: unknown mode " << it->second << endl,
839 lci.d_slog->info(Logr::Error, "addAllowedAdditionalQType: unknown mode", "mode", Logging::Loggable( it->second)));
840 }
841 }
842 }
843 lci.allowAdditionalQTypes.insert_or_assign(qtype, pair(targets, mode));
844 });
845
846 Lua->writeFunction("addProxyMapping", [&proxyMapping,&lci](const string& netmaskArg, const string& addressArg, boost::optional<std::vector<pair<int,std::string>>> smnStrings) {
847 try {
848 Netmask netmask(netmaskArg);
849 ComboAddress address(addressArg);
850 boost::optional<SuffixMatchNode> smn;
851 if (smnStrings) {
852 smn = boost::make_optional(SuffixMatchNode{});
853 for (const auto& el : *smnStrings) {
854 smn->add(el.second);
855 }
856 }
857 proxyMapping.insert_or_assign(netmask, {address, smn});
858 }
859 catch (std::exception& e) {
860 SLOG(g_log << Logger::Error << "Error processing addProxyMapping: " << e.what() << endl,
861 lci.d_slog->error(Logr::Error, e.what(), "Exception processing addProxyMapping", "exception", Logging::Loggable("std::exception")));
862 }
863 catch (PDNSException& e) {
864 SLOG(g_log << Logger::Error << "Error processing addProxyMapping: " << e.reason << endl,
865 lci.d_slog->error(Logr::Error, e.reason, "Exception processing addProxyMapping", "exception", Logging::Loggable("PDNSException")));
866 }
867 });
868
869 try {
870 Lua->executeCode(ifs);
871 g_luaconfs.setState(std::move(lci));
872 }
873 catch (const LuaContext::ExecutionErrorException& e) {
874 SLOG(g_log << Logger::Error << "Unable to load Lua script from '" + fname + "': ",
875 lci.d_slog->error(Logr::Error, e.what(), "Unable to load Lua script", "file", Logging::Loggable(fname)));
876 try {
877 std::rethrow_if_nested(e);
878 }
879 catch (const std::exception& exp) {
880 // exp is the exception that was thrown from inside the lambda
881 SLOG(g_log << exp.what() << std::endl,
882 lci.d_slog->error(Logr::Error, exp.what(), "Exception loading Lua", "exception", Logging::Loggable("std::exception")));
883 }
884 catch (const PDNSException& exp) {
885 // exp is the exception that was thrown from inside the lambda
886 SLOG(g_log << exp.reason << std::endl,
887 lci.d_slog->error(Logr::Error, exp.reason, "Exception loading Lua", "exception", Logging::Loggable("PDNSException"))) }
888 throw;
889 }
890 catch (std::exception& err) {
891 SLOG(g_log << Logger::Error << "Unable to load Lua script from '" + fname + "': " << err.what() << endl,
892 lci.d_slog->error(Logr::Error, err.what(), "Unable to load Lua script", "file", Logging::Loggable(fname), "exception", Logging::Loggable("std::exception")));
893 throw;
894 }
895 }
896
897 void startLuaConfigDelayedThreads(const luaConfigDelayedThreads& delayedThreads, uint64_t generation)
898 {
899 for (const auto& rpzPrimary : delayedThreads.rpzPrimaryThreads) {
900 try {
901 // The get calls all return a value object here. That is essential, since we want copies so that RPZIXFRTracker gets values
902 // with the proper lifetime.
903 std::thread t(RPZIXFRTracker, std::get<0>(rpzPrimary), std::get<1>(rpzPrimary), std::get<2>(rpzPrimary), std::get<3>(rpzPrimary), std::get<4>(rpzPrimary), std::get<5>(rpzPrimary), std::get<6>(rpzPrimary) * 1024 * 1024, std::get<7>(rpzPrimary), std::get<8>(rpzPrimary), std::get<9>(rpzPrimary), std::get<10>(rpzPrimary), std::get<11>(rpzPrimary), generation);
904 t.detach();
905 }
906 catch (const std::exception& e) {
907 SLOG(g_log << Logger::Error << "Problem starting RPZIXFRTracker thread: " << e.what() << endl,
908 g_slog->withName("rpz")->error(Logr::Error, e.what(), "Exception starting RPZIXFRTracker thread", "exception", Logging::Loggable("std::exception")));
909 exit(1);
910 }
911 catch (const PDNSException& e) {
912 SLOG(g_log << Logger::Error << "Problem starting RPZIXFRTracker thread: " << e.reason << endl,
913 g_slog->withName("rpz")->error(Logr::Error, e.reason, "Exception starting RPZIXFRTracker thread", "exception", Logging::Loggable("PDNSException")));
914 exit(1);
915 }
916 }
917 }