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
=unique_ptr
<DSRecordContent
>(dynamic_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
);
55 static 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
)
57 if(have
.count("policyName")) {
58 polName
= boost::get
<std::string
>(constGet(have
, "policyName"));
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
) {
66 DNSRecordContent::mastermake(QType::CNAME
, 1,
67 boost::get
<string
>(constGet(have
,"defcontent"))
70 if(have
.count("defttl"))
71 defpol
->d_ttl
= static_cast<int32_t>(boost::get
<uint32_t>(constGet(have
, "defttl")));
73 defpol
->d_ttl
= -1; // get it from the zone
76 if(have
.count("maxTTL")) {
77 maxTTL
= boost::get
<uint32_t>(constGet(have
, "maxTTL"));
79 if(have
.count("zoneSizeHint")) {
80 zoneSizeHint
= static_cast<size_t>(boost::get
<uint32_t>(constGet(have
, "zoneSizeHint")));
84 void loadRecursorLuaConfig(const std::string
& fname
, luaConfigDelayedThreads
& delayedThreads
)
93 throw PDNSException("Cannot open file '"+fname
+"': "+strerror(errno
));
95 auto luaconfsLocal
= g_luaconfs
.getLocal();
96 lci
.generation
= luaconfsLocal
->generation
+ 1;
98 Lua
.writeFunction("clearSortlist", [&lci
]() { lci
.sortlist
.clear(); });
100 /* we can get: "1.2.3.4"
101 {"1.2.3.4", "4.5.6.7"}
102 {"1.2.3.4", {"4.5.6.7", "8.9.10.11"}}
105 map
<string
,DNSFilterEngine::PolicyKind
> pmap
{
106 {"NoAction", DNSFilterEngine::PolicyKind::NoAction
},
107 {"Drop", DNSFilterEngine::PolicyKind::Drop
},
108 {"NXDOMAIN", DNSFilterEngine::PolicyKind::NXDOMAIN
},
109 {"NODATA", DNSFilterEngine::PolicyKind::NODATA
},
110 {"Truncate", DNSFilterEngine::PolicyKind::Truncate
},
111 {"Custom", DNSFilterEngine::PolicyKind::Custom
}
113 Lua
.writeVariable("Policy", pmap
);
115 Lua
.writeFunction("rpzFile", [&lci
](const string
& filename
, const boost::optional
<std::unordered_map
<string
,boost::variant
<uint32_t, string
>>>& options
) {
117 boost::optional
<DNSFilterEngine::Policy
> defpol
;
118 std::string
polName("rpzFile");
119 std::shared_ptr
<DNSFilterEngine::Zone
> zone
= std::make_shared
<DNSFilterEngine::Zone
>();
120 uint32_t maxTTL
= std::numeric_limits
<uint32_t>::max();
122 auto& have
= *options
;
123 size_t zoneSizeHint
= 0;
124 parseRPZParameters(have
, polName
, defpol
, maxTTL
, zoneSizeHint
);
125 if (zoneSizeHint
> 0) {
126 zone
->reserve(zoneSizeHint
);
129 g_log
<<Logger::Warning
<<"Loading RPZ from file '"<<filename
<<"'"<<endl
;
130 zone
->setName(polName
);
131 loadRPZFromFile(filename
, zone
, defpol
, maxTTL
);
132 lci
.dfe
.addZone(zone
);
133 g_log
<<Logger::Warning
<<"Done loading RPZ from file '"<<filename
<<"'"<<endl
;
135 catch(const std::exception
& e
) {
136 g_log
<<Logger::Error
<<"Unable to load RPZ zone from '"<<filename
<<"': "<<e
.what()<<endl
;
140 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
) {
142 boost::optional
<DNSFilterEngine::Policy
> defpol
;
143 std::shared_ptr
<DNSFilterEngine::Zone
> zone
= std::make_shared
<DNSFilterEngine::Zone
>();
146 size_t maxReceivedXFRMBytes
= 0;
147 uint16_t axfrTimeout
= 20;
148 uint32_t maxTTL
= std::numeric_limits
<uint32_t>::max();
149 ComboAddress localAddress
;
150 std::vector
<ComboAddress
> masters
;
151 if (masters_
.type() == typeid(string
)) {
152 masters
.push_back(ComboAddress(boost::get
<std::string
>(masters_
), 53));
155 for (const auto& master
: boost::get
<std::vector
<std::pair
<int, std::string
>>>(masters_
)) {
156 masters
.push_back(ComboAddress(master
.second
, 53));
161 std::string dumpFile
;
162 std::shared_ptr
<SOARecordContent
> sr
= nullptr;
165 std::string seedFile
;
166 std::string
polName(zoneName
);
169 auto& have
= *options
;
170 size_t zoneSizeHint
= 0;
171 parseRPZParameters(have
, polName
, defpol
, maxTTL
, zoneSizeHint
);
172 if (zoneSizeHint
> 0) {
173 zone
->reserve(zoneSizeHint
);
176 if(have
.count("tsigname")) {
177 tt
.name
=DNSName(toLower(boost::get
<string
>(constGet(have
, "tsigname"))));
178 tt
.algo
=DNSName(toLower(boost::get
<string
>(constGet(have
, "tsigalgo"))));
179 if(B64Decode(boost::get
<string
>(constGet(have
, "tsigsecret")), tt
.secret
))
180 throw std::runtime_error("TSIG secret is not valid Base-64 encoded");
183 if(have
.count("refresh")) {
184 refresh
= boost::get
<uint32_t>(constGet(have
,"refresh"));
187 if(have
.count("maxReceivedMBytes")) {
188 maxReceivedXFRMBytes
= static_cast<size_t>(boost::get
<uint32_t>(constGet(have
,"maxReceivedMBytes")));
191 if(have
.count("localAddress")) {
192 localAddress
= ComboAddress(boost::get
<string
>(constGet(have
,"localAddress")));
195 if(have
.count("axfrTimeout")) {
196 axfrTimeout
= static_cast<uint16_t>(boost::get
<uint32_t>(constGet(have
, "axfrTimeout")));
199 if(have
.count("seedFile")) {
200 seedFile
= boost::get
<std::string
>(constGet(have
, "seedFile"));
203 if(have
.count("dumpFile")) {
204 dumpFile
= boost::get
<std::string
>(constGet(have
, "dumpFile"));
208 if (localAddress
!= ComboAddress()) {
209 // We were passed a localAddress, check if its AF matches the masters'
210 for (const auto& master
: masters
) {
211 if (localAddress
.sin4
.sin_family
!= master
.sin4
.sin_family
) {
212 throw PDNSException("Master address("+master
.toString()+") is not of the same Address Family as the local address ("+localAddress
.toString()+").");
217 DNSName
domain(zoneName
);
218 zone
->setDomain(domain
);
219 zone
->setName(polName
);
220 zone
->setRefresh(refresh
);
221 zoneIdx
= lci
.dfe
.addZone(zone
);
223 if (!seedFile
.empty()) {
224 g_log
<<Logger::Info
<<"Pre-loading RPZ zone "<<zoneName
<<" from seed file '"<<seedFile
<<"'"<<endl
;
226 sr
= loadRPZFromFile(seedFile
, zone
, defpol
, maxTTL
);
228 if (zone
->getDomain() != domain
) {
229 throw PDNSException("The RPZ zone " + zoneName
+ " loaded from the seed file (" + zone
->getDomain().toString() + ") does not match the one passed in parameter (" + domain
.toString() + ")");
233 throw PDNSException("The RPZ zone " + zoneName
+ " loaded from the seed file (" + zone
->getDomain().toString() + ") has no SOA record");
236 catch(const std::exception
& e
) {
237 g_log
<<Logger::Warning
<<"Unable to pre-load RPZ zone "<<zoneName
<<" from seed file '"<<seedFile
<<"': "<<e
.what()<<endl
;
241 catch(const std::exception
& e
) {
242 g_log
<<Logger::Error
<<"Problem configuring 'rpzMaster': "<<e
.what()<<endl
;
243 exit(1); // FIXME proper exit code?
245 catch(const PDNSException
& e
) {
246 g_log
<<Logger::Error
<<"Problem configuring 'rpzMaster': "<<e
.reason
<<endl
;
247 exit(1); // FIXME proper exit code?
250 delayedThreads
.rpzMasterThreads
.push_back(std::make_tuple(masters
, defpol
, maxTTL
, zoneIdx
, tt
, maxReceivedXFRMBytes
, localAddress
, axfrTimeout
, sr
, dumpFile
));
253 typedef vector
<pair
<int,boost::variant
<string
, vector
<pair
<int, string
> > > > > argvec_t
;
254 Lua
.writeFunction("addSortList",
255 [&lci
](const std::string
& formask_
,
256 const boost::variant
<string
, argvec_t
>& masks
,
257 boost::optional
<int> order_
)
260 Netmask
formask(formask_
);
261 int order
= order_
? (*order_
) : lci
.sortlist
.getMaxOrder(formask
)+1;
262 if(auto str
= boost::get
<string
>(&masks
))
263 lci
.sortlist
.addEntry(formask
, Netmask(*str
), order
);
266 auto vec
= boost::get
<argvec_t
>(&masks
);
267 for(const auto& e
: *vec
) {
268 if(auto s
= boost::get
<string
>(&e
.second
)) {
269 lci
.sortlist
.addEntry(formask
, Netmask(*s
), order
);
272 const auto& v
=boost::get
<vector
<pair
<int, string
> > >(e
.second
);
273 for(const auto& entry
: v
)
274 lci
.sortlist
.addEntry(formask
, Netmask(entry
.second
), order
);
280 catch(std::exception
& e
) {
281 g_log
<<Logger::Error
<<"Error in addSortList: "<<e
.what()<<endl
;
285 Lua
.writeFunction("addDS", [&lci
](const std::string
& who
, const std::string
& what
) {
286 warnIfDNSSECDisabled("Warning: adding Trust Anchor for DNSSEC (addDS), but dnssec is set to 'off'!");
288 auto ds
= unique_ptr
<DSRecordContent
>(dynamic_cast<DSRecordContent
*>(DSRecordContent::make(what
)));
289 lci
.dsAnchors
[zone
].insert(*ds
);
292 Lua
.writeFunction("clearDS", [&lci
](boost::optional
<string
> who
) {
293 warnIfDNSSECDisabled("Warning: removing Trust Anchor for DNSSEC (clearDS), but dnssec is set to 'off'!");
295 lci
.dsAnchors
.erase(DNSName(*who
));
297 lci
.dsAnchors
.clear();
300 Lua
.writeFunction("addNTA", [&lci
](const std::string
& who
, const boost::optional
<std::string
> why
) {
301 warnIfDNSSECDisabled("Warning: adding Negative Trust Anchor for DNSSEC (addNTA), but dnssec is set to 'off'!");
303 lci
.negAnchors
[DNSName(who
)] = static_cast<string
>(*why
);
305 lci
.negAnchors
[DNSName(who
)] = "";
308 Lua
.writeFunction("clearNTA", [&lci
](boost::optional
<string
> who
) {
309 warnIfDNSSECDisabled("Warning: removing Negative Trust Anchor for DNSSEC (clearNTA), but dnssec is set to 'off'!");
311 lci
.negAnchors
.erase(DNSName(*who
));
313 lci
.negAnchors
.clear();
317 Lua
.writeFunction("protobufServer", [&lci
](const string
& server_
, const boost::optional
<uint16_t> timeout
, const boost::optional
<uint64_t> maxQueuedEntries
, const boost::optional
<uint8_t> reconnectWaitTime
, const boost::optional
<uint8_t> maskV4
, boost::optional
<uint8_t> maskV6
, boost::optional
<bool> asyncConnect
, boost::optional
<bool> taggedOnly
) {
319 ComboAddress
server(server_
);
320 if (!lci
.protobufExportConfig
.enabled
) {
322 lci
.protobufExportConfig
.enabled
= true;
324 lci
.protobufExportConfig
.server
= server
;
327 lci
.protobufExportConfig
.timeout
= *timeout
;
330 if (maxQueuedEntries
) {
331 lci
.protobufExportConfig
.maxQueuedEntries
= *maxQueuedEntries
;
334 if (reconnectWaitTime
) {
335 lci
.protobufExportConfig
.reconnectWaitTime
= *reconnectWaitTime
;
339 lci
.protobufExportConfig
.asyncConnect
= *asyncConnect
;
343 lci
.protobufMaskV4
= *maskV4
;
346 lci
.protobufMaskV6
= *maskV6
;
349 lci
.protobufTaggedOnly
= *taggedOnly
;
353 g_log
<<Logger::Error
<<"Only one protobuf server can be configured, we already have "<<lci
.protobufExportConfig
.server
.toString()<<endl
;
356 catch(std::exception
& e
) {
357 g_log
<<Logger::Error
<<"Error while starting protobuf logger to '"<<server_
<<": "<<e
.what()<<endl
;
359 catch(PDNSException
& e
) {
360 g_log
<<Logger::Error
<<"Error while starting protobuf logger to '"<<server_
<<": "<<e
.reason
<<endl
;
364 Lua
.writeFunction("outgoingProtobufServer", [&lci
](const string
& server_
, const boost::optional
<uint16_t> timeout
, const boost::optional
<uint64_t> maxQueuedEntries
, const boost::optional
<uint8_t> reconnectWaitTime
, boost::optional
<bool> asyncConnect
) {
366 ComboAddress
server(server_
);
367 if (!lci
.outgoingProtobufExportConfig
.enabled
) {
369 lci
.outgoingProtobufExportConfig
.enabled
= true;
371 lci
.outgoingProtobufExportConfig
.server
= server
;
374 lci
.outgoingProtobufExportConfig
.timeout
= *timeout
;
377 if (maxQueuedEntries
) {
378 lci
.outgoingProtobufExportConfig
.maxQueuedEntries
= *maxQueuedEntries
;
381 if (reconnectWaitTime
) {
382 lci
.outgoingProtobufExportConfig
.reconnectWaitTime
= *reconnectWaitTime
;
386 lci
.outgoingProtobufExportConfig
.asyncConnect
= *asyncConnect
;
390 g_log
<<Logger::Error
<<"Only one protobuf server can be configured, we already have "<<lci
.outgoingProtobufExportConfig
.server
.toString()<<endl
;
393 catch(std::exception
& e
) {
394 g_log
<<Logger::Error
<<"Error while starting protobuf logger to '"<<server_
<<": "<<e
.what()<<endl
;
396 catch(PDNSException
& e
) {
397 g_log
<<Logger::Error
<<"Error while starting protobuf logger to '"<<server_
<<": "<<e
.reason
<<endl
;
403 Lua
.executeCode(ifs
);
404 g_luaconfs
.setState(lci
);
406 catch(const LuaContext::ExecutionErrorException
& e
) {
407 g_log
<<Logger::Error
<<"Unable to load Lua script from '"+fname
+"': ";
409 std::rethrow_if_nested(e
);
410 } catch(const std::exception
& exp
) {
411 // exp is the exception that was thrown from inside the lambda
412 g_log
<< exp
.what() << std::endl
;
414 catch(const PDNSException
& exp
) {
415 // exp is the exception that was thrown from inside the lambda
416 g_log
<< exp
.reason
<< std::endl
;
421 catch(std::exception
& err
) {
422 g_log
<<Logger::Error
<<"Unable to load Lua script from '"+fname
+"': "<<err
.what()<<endl
;
428 void startLuaConfigDelayedThreads(const luaConfigDelayedThreads
& delayedThreads
, uint64_t generation
)
430 for (const auto& rpzMaster
: delayedThreads
.rpzMasterThreads
) {
432 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
);
435 catch(const std::exception
& e
) {
436 g_log
<<Logger::Error
<<"Problem starting RPZIXFRTracker thread: "<<e
.what()<<endl
;
437 exit(1); // FIXME proper exit code?
439 catch(const PDNSException
& e
) {
440 g_log
<<Logger::Error
<<"Problem starting RPZIXFRTracker thread: "<<e
.reason
<<endl
;
441 exit(1); // FIXME proper exit code?