2 #include "ext/luawrapper/include/LuaContext.hpp"
6 #include "namespaces.hh"
8 #include "rec-lua-conf.hh"
10 #include "filterpo.hh"
12 #include "rpzloader.hh"
14 #include "remote_logger.hh"
15 #include "validate.hh"
16 #include "validate-recursor.hh"
17 #include "root-dnssec.hh"
19 GlobalStateHolder
<LuaConfigItems
> g_luaconfs
;
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.
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.
28 If someone loads an empty Lua file, the default LuaConfigItems struct MUST MAKE SENSE.
30 To make this easy on you, here is a LuaConfigItems constructor where you
31 can set sane defaults:
34 LuaConfigItems::LuaConfigItems()
36 DNSName
root("."); // don't use g_rootdnsname here, it might not exist yet
37 for (const auto &dsRecord
: rootDSs
) {
38 auto ds
=std::dynamic_pointer_cast
<DSRecordContent
>(DSRecordContent::make(dsRecord
));
39 dsAnchors
[root
].insert(*ds
);
43 /* DID YOU READ THE STORY ABOVE? */
46 typename
C::value_type::second_type
constGet(const C
& c
, const std::string
& name
)
48 auto iter
= c
.find(name
);
54 typedef std::unordered_map
<std::string
, boost::variant
<bool, uint32_t, std::string
> > rpzOptions_t
;
56 static void parseRPZParameters(rpzOptions_t
& have
, std::string
& polName
, boost::optional
<DNSFilterEngine::Policy
>& defpol
, bool& defpolOverrideLocal
, uint32_t& maxTTL
, size_t& zoneSizeHint
)
58 if(have
.count("policyName")) {
59 polName
= boost::get
<std::string
>(have
["policyName"]);
61 if(have
.count("defpol")) {
62 defpol
=DNSFilterEngine::Policy();
63 defpol
->d_kind
= (DNSFilterEngine::PolicyKind
)boost::get
<uint32_t>(have
["defpol"]);
64 defpol
->d_name
= std::make_shared
<std::string
>(polName
);
65 if(defpol
->d_kind
== DNSFilterEngine::PolicyKind::Custom
) {
66 defpol
->d_custom
.push_back(DNSRecordContent::mastermake(QType::CNAME
, QClass::IN
,
67 boost::get
<string
>(have
["defcontent"])));
69 if(have
.count("defttl"))
70 defpol
->d_ttl
= static_cast<int32_t>(boost::get
<uint32_t>(have
["defttl"]));
72 defpol
->d_ttl
= -1; // get it from the zone
75 if (have
.count("defpolOverrideLocalData")) {
76 defpolOverrideLocal
= boost::get
<bool>(have
["defpolOverrideLocalData"]);
79 if(have
.count("maxTTL")) {
80 maxTTL
= boost::get
<uint32_t>(have
["maxTTL"]);
82 if(have
.count("zoneSizeHint")) {
83 zoneSizeHint
= static_cast<size_t>(boost::get
<uint32_t>(have
["zoneSizeHint"]));
88 typedef std::unordered_map
<std::string
, boost::variant
<bool, uint64_t, std::string
, std::vector
<std::pair
<int,std::string
> > > > protobufOptions_t
;
90 static void parseProtobufOptions(boost::optional
<protobufOptions_t
> vars
, ProtobufExportConfig
& config
)
96 if (vars
->count("timeout")) {
97 config
.timeout
= boost::get
<uint64_t>((*vars
)["timeout"]);
100 if (vars
->count("maxQueuedEntries")) {
101 config
.maxQueuedEntries
= boost::get
<uint64_t>((*vars
)["maxQueuedEntries"]);
104 if (vars
->count("reconnectWaitTime")) {
105 config
.reconnectWaitTime
= boost::get
<uint64_t>((*vars
)["reconnectWaitTime"]);
108 if (vars
->count("asyncConnect")) {
109 config
.asyncConnect
= boost::get
<bool>((*vars
)["asyncConnect"]);
112 if (vars
->count("taggedOnly")) {
113 config
.taggedOnly
= boost::get
<bool>((*vars
)["taggedOnly"]);
116 if (vars
->count("logQueries")) {
117 config
.logQueries
= boost::get
<bool>((*vars
)["logQueries"]);
120 if (vars
->count("logResponses")) {
121 config
.logResponses
= boost::get
<bool>((*vars
)["logResponses"]);
124 if (vars
->count("exportTypes")) {
125 config
.exportTypes
.clear();
127 auto types
= boost::get
<std::vector
<std::pair
<int, std::string
>>>((*vars
)["exportTypes"]);
128 for (const auto& pair
: types
) {
129 const auto type
= pair
.second
;
132 for (const auto& entry
: QType::names
) {
133 if (entry
.first
== type
) {
135 config
.exportTypes
.insert(entry
.second
);
141 throw std::runtime_error("Unknown QType '" + type
+ "' in protobuf's export types");
146 #endif /* HAVE_PROTOBUF */
148 void loadRecursorLuaConfig(const std::string
& fname
, luaConfigDelayedThreads
& delayedThreads
)
157 throw PDNSException("Cannot open file '"+fname
+"': "+strerror(errno
));
159 auto luaconfsLocal
= g_luaconfs
.getLocal();
160 lci
.generation
= luaconfsLocal
->generation
+ 1;
162 // pdnslog here is compatible with pdnslog in lua-base4.cc.
163 Lua
.writeFunction("pdnslog", [](const std::string
& msg
, boost::optional
<int> loglevel
) { g_log
<< (Logger::Urgency
)loglevel
.get_value_or(Logger::Warning
) << msg
<<endl
; });
164 std::unordered_map
<string
, std::unordered_map
<string
, int>> pdns_table
;
165 pdns_table
["loglevels"] = std::unordered_map
<string
, int>{
166 {"Alert", LOG_ALERT
},
167 {"Critical", LOG_CRIT
},
168 {"Debug", LOG_DEBUG
},
169 {"Emergency", LOG_EMERG
},
171 {"Notice", LOG_NOTICE
},
172 {"Warning", LOG_WARNING
},
175 Lua
.writeVariable("pdns", pdns_table
);
177 Lua
.writeFunction("clearSortlist", [&lci
]() { lci
.sortlist
.clear(); });
179 /* we can get: "1.2.3.4"
180 {"1.2.3.4", "4.5.6.7"}
181 {"1.2.3.4", {"4.5.6.7", "8.9.10.11"}}
184 map
<string
,DNSFilterEngine::PolicyKind
> pmap
{
185 {"NoAction", DNSFilterEngine::PolicyKind::NoAction
},
186 {"Drop", DNSFilterEngine::PolicyKind::Drop
},
187 {"NXDOMAIN", DNSFilterEngine::PolicyKind::NXDOMAIN
},
188 {"NODATA", DNSFilterEngine::PolicyKind::NODATA
},
189 {"Truncate", DNSFilterEngine::PolicyKind::Truncate
},
190 {"Custom", DNSFilterEngine::PolicyKind::Custom
}
192 Lua
.writeVariable("Policy", pmap
);
194 Lua
.writeFunction("rpzFile", [&lci
](const string
& filename
, boost::optional
<rpzOptions_t
> options
) {
196 boost::optional
<DNSFilterEngine::Policy
> defpol
;
197 bool defpolOverrideLocal
= true;
198 std::string
polName("rpzFile");
199 std::shared_ptr
<DNSFilterEngine::Zone
> zone
= std::make_shared
<DNSFilterEngine::Zone
>();
200 uint32_t maxTTL
= std::numeric_limits
<uint32_t>::max();
202 auto& have
= *options
;
203 size_t zoneSizeHint
= 0;
204 parseRPZParameters(have
, polName
, defpol
, defpolOverrideLocal
, maxTTL
, zoneSizeHint
);
205 if (zoneSizeHint
> 0) {
206 zone
->reserve(zoneSizeHint
);
209 g_log
<<Logger::Warning
<<"Loading RPZ from file '"<<filename
<<"'"<<endl
;
210 zone
->setName(polName
);
211 loadRPZFromFile(filename
, zone
, defpol
, defpolOverrideLocal
, maxTTL
);
212 lci
.dfe
.addZone(zone
);
213 g_log
<<Logger::Warning
<<"Done loading RPZ from file '"<<filename
<<"'"<<endl
;
215 catch(const std::exception
& e
) {
216 g_log
<<Logger::Error
<<"Unable to load RPZ zone from '"<<filename
<<"': "<<e
.what()<<endl
;
220 Lua
.writeFunction("rpzMaster", [&lci
, &delayedThreads
](const boost::variant
<string
, std::vector
<std::pair
<int, string
> > >& masters_
, const string
& zoneName
, boost::optional
<rpzOptions_t
> options
) {
222 boost::optional
<DNSFilterEngine::Policy
> defpol
;
223 bool defpolOverrideLocal
= true;
224 std::shared_ptr
<DNSFilterEngine::Zone
> zone
= std::make_shared
<DNSFilterEngine::Zone
>();
227 size_t maxReceivedXFRMBytes
= 0;
228 uint16_t axfrTimeout
= 20;
229 uint32_t maxTTL
= std::numeric_limits
<uint32_t>::max();
230 ComboAddress localAddress
;
231 std::vector
<ComboAddress
> masters
;
232 if (masters_
.type() == typeid(string
)) {
233 masters
.push_back(ComboAddress(boost::get
<std::string
>(masters_
), 53));
236 for (const auto& master
: boost::get
<std::vector
<std::pair
<int, std::string
>>>(masters_
)) {
237 masters
.push_back(ComboAddress(master
.second
, 53));
242 std::string dumpFile
;
243 std::shared_ptr
<SOARecordContent
> sr
= nullptr;
246 std::string seedFile
;
247 std::string
polName(zoneName
);
250 auto& have
= *options
;
251 size_t zoneSizeHint
= 0;
252 parseRPZParameters(have
, polName
, defpol
, defpolOverrideLocal
, maxTTL
, zoneSizeHint
);
253 if (zoneSizeHint
> 0) {
254 zone
->reserve(zoneSizeHint
);
257 if(have
.count("tsigname")) {
258 tt
.name
=DNSName(toLower(boost::get
<string
>(have
["tsigname"])));
259 tt
.algo
=DNSName(toLower(boost::get
<string
>(have
[ "tsigalgo"])));
260 if(B64Decode(boost::get
<string
>(have
[ "tsigsecret"]), tt
.secret
))
261 throw std::runtime_error("TSIG secret is not valid Base-64 encoded");
264 if(have
.count("refresh")) {
265 refresh
= boost::get
<uint32_t>(have
["refresh"]);
268 if(have
.count("maxReceivedMBytes")) {
269 maxReceivedXFRMBytes
= static_cast<size_t>(boost::get
<uint32_t>(have
["maxReceivedMBytes"]));
272 if(have
.count("localAddress")) {
273 localAddress
= ComboAddress(boost::get
<string
>(have
["localAddress"]));
276 if(have
.count("axfrTimeout")) {
277 axfrTimeout
= static_cast<uint16_t>(boost::get
<uint32_t>(have
["axfrTimeout"]));
280 if(have
.count("seedFile")) {
281 seedFile
= boost::get
<std::string
>(have
["seedFile"]);
284 if(have
.count("dumpFile")) {
285 dumpFile
= boost::get
<std::string
>(have
["dumpFile"]);
289 if (localAddress
!= ComboAddress()) {
290 // We were passed a localAddress, check if its AF matches the masters'
291 for (const auto& master
: masters
) {
292 if (localAddress
.sin4
.sin_family
!= master
.sin4
.sin_family
) {
293 throw PDNSException("Master address("+master
.toString()+") is not of the same Address Family as the local address ("+localAddress
.toString()+").");
298 DNSName
domain(zoneName
);
299 zone
->setDomain(domain
);
300 zone
->setName(polName
);
301 zone
->setRefresh(refresh
);
302 zoneIdx
= lci
.dfe
.addZone(zone
);
304 if (!seedFile
.empty()) {
305 g_log
<<Logger::Info
<<"Pre-loading RPZ zone "<<zoneName
<<" from seed file '"<<seedFile
<<"'"<<endl
;
307 sr
= loadRPZFromFile(seedFile
, zone
, defpol
, defpolOverrideLocal
, maxTTL
);
309 if (zone
->getDomain() != domain
) {
310 throw PDNSException("The RPZ zone " + zoneName
+ " loaded from the seed file (" + zone
->getDomain().toString() + ") does not match the one passed in parameter (" + domain
.toString() + ")");
314 throw PDNSException("The RPZ zone " + zoneName
+ " loaded from the seed file (" + zone
->getDomain().toString() + ") has no SOA record");
317 catch(const std::exception
& e
) {
318 g_log
<<Logger::Warning
<<"Unable to pre-load RPZ zone "<<zoneName
<<" from seed file '"<<seedFile
<<"': "<<e
.what()<<endl
;
322 catch(const std::exception
& e
) {
323 g_log
<<Logger::Error
<<"Problem configuring 'rpzMaster': "<<e
.what()<<endl
;
324 exit(1); // FIXME proper exit code?
326 catch(const PDNSException
& e
) {
327 g_log
<<Logger::Error
<<"Problem configuring 'rpzMaster': "<<e
.reason
<<endl
;
328 exit(1); // FIXME proper exit code?
331 delayedThreads
.rpzMasterThreads
.push_back(std::make_tuple(masters
, defpol
, defpolOverrideLocal
, maxTTL
, zoneIdx
, tt
, maxReceivedXFRMBytes
, localAddress
, axfrTimeout
, sr
, dumpFile
));
334 typedef vector
<pair
<int,boost::variant
<string
, vector
<pair
<int, string
> > > > > argvec_t
;
335 Lua
.writeFunction("addSortList",
336 [&lci
](const std::string
& formask_
,
337 const boost::variant
<string
, argvec_t
>& masks
,
338 boost::optional
<int> order_
)
341 Netmask
formask(formask_
);
342 int order
= order_
? (*order_
) : lci
.sortlist
.getMaxOrder(formask
)+1;
343 if(auto str
= boost::get
<string
>(&masks
))
344 lci
.sortlist
.addEntry(formask
, Netmask(*str
), order
);
347 auto vec
= boost::get
<argvec_t
>(&masks
);
348 for(const auto& e
: *vec
) {
349 if(auto s
= boost::get
<string
>(&e
.second
)) {
350 lci
.sortlist
.addEntry(formask
, Netmask(*s
), order
);
353 const auto& v
=boost::get
<vector
<pair
<int, string
> > >(e
.second
);
354 for(const auto& entry
: v
)
355 lci
.sortlist
.addEntry(formask
, Netmask(entry
.second
), order
);
361 catch(std::exception
& e
) {
362 g_log
<<Logger::Error
<<"Error in addSortList: "<<e
.what()<<endl
;
366 Lua
.writeFunction("addTA", [&lci
](const std::string
& who
, const std::string
& what
) {
367 warnIfDNSSECDisabled("Warning: adding Trust Anchor for DNSSEC (addTA), but dnssec is set to 'off'!");
369 auto ds
= std::dynamic_pointer_cast
<DSRecordContent
>(DSRecordContent::make(what
));
370 lci
.dsAnchors
[zone
].insert(*ds
);
373 Lua
.writeFunction("clearTA", [&lci
](boost::optional
<string
> who
) {
374 warnIfDNSSECDisabled("Warning: removing Trust Anchor for DNSSEC (clearTA), but dnssec is set to 'off'!");
376 lci
.dsAnchors
.erase(DNSName(*who
));
378 lci
.dsAnchors
.clear();
382 Lua
.writeFunction("addDS", [&lci
](const std::string
& who
, const std::string
& what
) {
383 warnIfDNSSECDisabled("Warning: adding Trust Anchor for DNSSEC (addDS), but dnssec is set to 'off'!");
384 g_log
<<Logger::Warning
<<"addDS is deprecated and will be removed in the future, switch to addTA"<<endl
;
386 auto ds
= std::dynamic_pointer_cast
<DSRecordContent
>(DSRecordContent::make(what
));
387 lci
.dsAnchors
[zone
].insert(*ds
);
391 Lua
.writeFunction("clearDS", [&lci
](boost::optional
<string
> who
) {
392 g_log
<<Logger::Warning
<<"clearDS is deprecated and will be removed in the future, switch to clearTA"<<endl
;
393 warnIfDNSSECDisabled("Warning: removing Trust Anchor for DNSSEC (clearDS), but dnssec is set to 'off'!");
395 lci
.dsAnchors
.erase(DNSName(*who
));
397 lci
.dsAnchors
.clear();
400 Lua
.writeFunction("addNTA", [&lci
](const std::string
& who
, const boost::optional
<std::string
> why
) {
401 warnIfDNSSECDisabled("Warning: adding Negative Trust Anchor for DNSSEC (addNTA), but dnssec is set to 'off'!");
403 lci
.negAnchors
[DNSName(who
)] = static_cast<string
>(*why
);
405 lci
.negAnchors
[DNSName(who
)] = "";
408 Lua
.writeFunction("clearNTA", [&lci
](boost::optional
<string
> who
) {
409 warnIfDNSSECDisabled("Warning: removing Negative Trust Anchor for DNSSEC (clearNTA), but dnssec is set to 'off'!");
411 lci
.negAnchors
.erase(DNSName(*who
));
413 lci
.negAnchors
.clear();
416 Lua
.writeFunction("readTrustAnchorsFromFile", [&lci
](const std::string
& fname
, const boost::optional
<uint32_t> interval
) {
417 uint32_t realInterval
= 24;
419 realInterval
= static_cast<uint32_t>(*interval
);
421 warnIfDNSSECDisabled("Warning: reading Trust Anchors from file (readTrustAnchorsFromFile), but dnssec is set to 'off'!");
422 lci
.trustAnchorFileInfo
.fname
= fname
;
423 lci
.trustAnchorFileInfo
.interval
= realInterval
;
424 updateTrustAnchorsFromFile(fname
, lci
.dsAnchors
);
428 Lua
.writeFunction("setProtobufMasks", [&lci
](const uint8_t maskV4
, uint8_t maskV6
) {
429 lci
.protobufMaskV4
= maskV4
;
430 lci
.protobufMaskV6
= maskV6
;
433 Lua
.writeFunction("protobufServer", [&lci
](boost::variant
<const std::string
, const std::unordered_map
<int, std::string
>> servers
, boost::optional
<protobufOptions_t
> vars
) {
434 if (!lci
.protobufExportConfig
.enabled
) {
436 lci
.protobufExportConfig
.enabled
= true;
439 if (servers
.type() == typeid(std::string
)) {
440 auto server
= boost::get
<const std::string
>(servers
);
442 lci
.protobufExportConfig
.servers
.emplace_back(server
);
445 auto serversMap
= boost::get
<const std::unordered_map
<int,std::string
>>(servers
);
446 for (const auto& serverPair
: serversMap
) {
447 lci
.protobufExportConfig
.servers
.emplace_back(serverPair
.second
);
451 parseProtobufOptions(vars
, lci
.protobufExportConfig
);
453 catch(std::exception
& e
) {
454 g_log
<<Logger::Error
<<"Error while adding protobuf logger: "<<e
.what()<<endl
;
456 catch(PDNSException
& e
) {
457 g_log
<<Logger::Error
<<"Error while adding protobuf logger: "<<e
.reason
<<endl
;
461 g_log
<<Logger::Error
<<"Only one protobufServer() directive can be configured, we already have "<<lci
.protobufExportConfig
.servers
.at(0).toString()<<endl
;
465 Lua
.writeFunction("outgoingProtobufServer", [&lci
](boost::variant
<const std::string
, const std::unordered_map
<int, std::string
>> servers
, boost::optional
<protobufOptions_t
> vars
) {
466 if (!lci
.outgoingProtobufExportConfig
.enabled
) {
468 lci
.outgoingProtobufExportConfig
.enabled
= true;
471 if (servers
.type() == typeid(std::string
)) {
472 auto server
= boost::get
<const std::string
>(servers
);
474 lci
.outgoingProtobufExportConfig
.servers
.emplace_back(server
);
477 auto serversMap
= boost::get
<const std::unordered_map
<int,std::string
>>(servers
);
478 for (const auto& serverPair
: serversMap
) {
479 lci
.outgoingProtobufExportConfig
.servers
.emplace_back(serverPair
.second
);
483 parseProtobufOptions(vars
, lci
.outgoingProtobufExportConfig
);
485 catch(std::exception
& e
) {
486 g_log
<<Logger::Error
<<"Error while starting outgoing protobuf logger: "<<e
.what()<<endl
;
488 catch(PDNSException
& e
) {
489 g_log
<<Logger::Error
<<"Error while starting outgoing protobuf logger: "<<e
.reason
<<endl
;
493 g_log
<<Logger::Error
<<"Only one outgoingProtobufServer() directive can be configured, we already have "<<lci
.outgoingProtobufExportConfig
.servers
.at(0).toString()<<endl
;
499 Lua
.executeCode(ifs
);
500 g_luaconfs
.setState(lci
);
502 catch(const LuaContext::ExecutionErrorException
& e
) {
503 g_log
<<Logger::Error
<<"Unable to load Lua script from '"+fname
+"': ";
505 std::rethrow_if_nested(e
);
506 } catch(const std::exception
& exp
) {
507 // exp is the exception that was thrown from inside the lambda
508 g_log
<< exp
.what() << std::endl
;
510 catch(const PDNSException
& exp
) {
511 // exp is the exception that was thrown from inside the lambda
512 g_log
<< exp
.reason
<< std::endl
;
517 catch(std::exception
& err
) {
518 g_log
<<Logger::Error
<<"Unable to load Lua script from '"+fname
+"': "<<err
.what()<<endl
;
524 void startLuaConfigDelayedThreads(const luaConfigDelayedThreads
& delayedThreads
, uint64_t generation
)
526 for (const auto& rpzMaster
: delayedThreads
.rpzMasterThreads
) {
528 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
), std::get
<6>(rpzMaster
) * 1024 * 1024, std::get
<7>(rpzMaster
), std::get
<8>(rpzMaster
), std::get
<9>(rpzMaster
), std::get
<10>(rpzMaster
), generation
);
531 catch(const std::exception
& e
) {
532 g_log
<<Logger::Error
<<"Problem starting RPZIXFRTracker thread: "<<e
.what()<<endl
;
533 exit(1); // FIXME proper exit code?
535 catch(const PDNSException
& e
) {
536 g_log
<<Logger::Error
<<"Problem starting RPZIXFRTracker thread: "<<e
.reason
<<endl
;
537 exit(1); // FIXME proper exit code?