]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/rec-lua-conf.cc
Merge pull request #6412 from rgacogne/dnsdist-gnutls-memset
[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
a4241908 84void loadRecursorLuaConfig(const std::string& fname, bool checkOnly)
3e61e7f7 85{
f3c18728 86 LuaConfigItems lci;
87
3e61e7f7 88 LuaContext Lua;
89 if(fname.empty())
90 return;
91 ifstream ifs(fname);
0f5785a6
PL
92 if(!ifs)
93 throw PDNSException("Cannot open file '"+fname+"': "+strerror(errno));
94
f3c18728 95 Lua.writeFunction("clearSortlist", [&lci]() { lci.sortlist.clear(); });
3e61e7f7 96
97 /* we can get: "1.2.3.4"
98 {"1.2.3.4", "4.5.6.7"}
99 {"1.2.3.4", {"4.5.6.7", "8.9.10.11"}}
100 */
101
ad42489c 102 map<string,DNSFilterEngine::PolicyKind> pmap{
103 {"NoAction", DNSFilterEngine::PolicyKind::NoAction},
104 {"Drop", DNSFilterEngine::PolicyKind::Drop},
105 {"NXDOMAIN", DNSFilterEngine::PolicyKind::NXDOMAIN},
106 {"NODATA", DNSFilterEngine::PolicyKind::NODATA},
107 {"Truncate", DNSFilterEngine::PolicyKind::Truncate},
108 {"Custom", DNSFilterEngine::PolicyKind::Custom}
109 };
110 Lua.writeVariable("Policy", pmap);
111
8f618901 112 Lua.writeFunction("rpzFile", [&lci](const string& filename, const boost::optional<std::unordered_map<string,boost::variant<uint32_t, string>>>& options) {
ad42489c 113 try {
563bbdca
RG
114 boost::optional<DNSFilterEngine::Policy> defpol;
115 std::string polName("rpzFile");
6b972d59 116 std::shared_ptr<DNSFilterEngine::Zone> zone = std::make_shared<DNSFilterEngine::Zone>();
563bbdca
RG
117 uint32_t maxTTL = std::numeric_limits<uint32_t>::max();
118 if(options) {
119 auto& have = *options;
8f618901
RG
120 size_t zoneSizeHint = 0;
121 parseRPZParameters(have, polName, defpol, maxTTL, zoneSizeHint);
122 if (zoneSizeHint > 0) {
6b972d59 123 zone->reserve(zoneSizeHint);
a2d0450e 124 }
563bbdca 125 }
dd079764 126 theL()<<Logger::Warning<<"Loading RPZ from file '"<<filename<<"'"<<endl;
6b972d59
RG
127 zone->setName(polName);
128 loadRPZFromFile(filename, zone, defpol, maxTTL);
129 lci.dfe.addZone(zone);
dd079764 130 theL()<<Logger::Warning<<"Done loading RPZ from file '"<<filename<<"'"<<endl;
ad42489c 131 }
6791663c 132 catch(const std::exception& e) {
563bbdca 133 theL()<<Logger::Error<<"Unable to load RPZ zone from '"<<filename<<"': "<<e.what()<<endl;
ad42489c 134 }
135 });
136
6b972d59 137 Lua.writeFunction("rpzMaster", [&lci, checkOnly](const string& master_, const string& zoneName, const boost::optional<std::unordered_map<string,boost::variant<uint32_t, string>>>& options) {
209955c7 138
139 boost::optional<DNSFilterEngine::Policy> defpol;
140 std::shared_ptr<DNSFilterEngine::Zone> zone = std::make_shared<DNSFilterEngine::Zone>();
141 TSIGTriplet tt;
142 uint32_t refresh=0;
143 size_t maxReceivedXFRMBytes = 0;
ea448a77 144 uint16_t axfrTimeout = 20;
209955c7 145 uint32_t maxTTL = std::numeric_limits<uint32_t>::max();
146 ComboAddress localAddress;
147 ComboAddress master(master_, 53);
148 size_t zoneIdx;
149
ad42489c 150 try {
6b972d59 151 std::string polName(zoneName);
563bbdca
RG
152 if(options) {
153 auto& have = *options;
8f618901
RG
154 size_t zoneSizeHint = 0;
155 parseRPZParameters(have, polName, defpol, maxTTL, zoneSizeHint);
156 if (zoneSizeHint > 0) {
6b972d59 157 zone->reserve(zoneSizeHint);
a2d0450e 158 }
563bbdca 159 if(have.count("tsigname")) {
98c9ec39 160 tt.name=DNSName(toLower(boost::get<string>(constGet(have, "tsigname"))));
161 tt.algo=DNSName(toLower(boost::get<string>(constGet(have, "tsigalgo"))));
162 if(B64Decode(boost::get<string>(constGet(have, "tsigsecret")), tt.secret))
163 throw std::runtime_error("TSIG secret is not valid Base-64 encoded");
164 }
165 if(have.count("refresh")) {
8f618901 166 refresh = boost::get<uint32_t>(constGet(have,"refresh"));
98c9ec39 167 }
db8f9152 168 if(have.count("maxReceivedMBytes")) {
8f618901 169 maxReceivedXFRMBytes = static_cast<size_t>(boost::get<uint32_t>(constGet(have,"maxReceivedMBytes")));
db8f9152 170 }
f6a8f7d7
PL
171 if(have.count("localAddress")) {
172 localAddress = ComboAddress(boost::get<string>(constGet(have,"localAddress")));
173 }
ea448a77
PL
174 if(have.count("axfrTimeout")) {
175 axfrTimeout = static_cast<uint16_t>(boost::get<uint32_t>(constGet(have, "axfrTimeout")));
176 }
563bbdca 177 }
6791663c 178 if (localAddress != ComboAddress() && localAddress.sin4.sin_family != master.sin4.sin_family) {
f6a8f7d7
PL
179 // We were passed a localAddress, check if its AF matches the master's
180 throw PDNSException("Master address("+master.toString()+") is not of the same Address Family as the local address ("+localAddress.toString()+").");
6791663c
RG
181 }
182
209955c7 183 zone->setDomain(DNSName(zoneName));
6b972d59 184 zone->setName(polName);
6791663c 185 zone->setRefresh(refresh);
209955c7 186 zoneIdx = lci.dfe.addZone(zone);
ad42489c 187 }
6791663c 188 catch(const std::exception& e) {
209955c7 189 theL()<<Logger::Error<<"Problem configuring 'rpzMaster': "<<e.what()<<endl;
190 exit(1); // FIXME proper exit code?
ad42489c 191 }
6791663c 192 catch(const PDNSException& e) {
209955c7 193 theL()<<Logger::Error<<"Problem configuring 'rpzMaster': "<<e.reason<<endl;
194 exit(1); // FIXME proper exit code?
ad42489c 195 }
196
209955c7 197 try {
198 if (!checkOnly) {
ea448a77 199 std::thread t(RPZIXFRTracker, master, defpol, maxTTL, zoneIdx, tt, maxReceivedXFRMBytes * 1024 * 1024, localAddress, zone, axfrTimeout);
209955c7 200 t.detach();
201 }
202 }
203 catch(const std::exception& e) {
204 theL()<<Logger::Error<<"Problem starting RPZIXFRTracker thread: "<<e.what()<<endl;
205 exit(1); // FIXME proper exit code?
206 }
207 catch(const PDNSException& e) {
208 theL()<<Logger::Error<<"Problem starting RPZIXFRTracker thread: "<<e.reason<<endl;
209 exit(1); // FIXME proper exit code?
210 }
ad42489c 211 });
212
3e61e7f7 213 typedef vector<pair<int,boost::variant<string, vector<pair<int, string> > > > > argvec_t;
214 Lua.writeFunction("addSortList",
f3c18728 215 [&lci](const std::string& formask_,
3e61e7f7 216 const boost::variant<string, argvec_t>& masks,
217 boost::optional<int> order_)
218 {
219 try {
220 Netmask formask(formask_);
f3c18728 221 int order = order_ ? (*order_) : lci.sortlist.getMaxOrder(formask)+1;
3e61e7f7 222 if(auto str = boost::get<string>(&masks))
f3c18728 223 lci.sortlist.addEntry(formask, Netmask(*str), order);
3e61e7f7 224 else {
225
226 auto vec = boost::get<argvec_t>(&masks);
227 for(const auto& e : *vec) {
228 if(auto s = boost::get<string>(&e.second)) {
f3c18728 229 lci.sortlist.addEntry(formask, Netmask(*s), order);
3e61e7f7 230 }
231 else {
232 const auto& v =boost::get<vector<pair<int, string> > >(e.second);
dd079764
RG
233 for(const auto& entry : v)
234 lci.sortlist.addEntry(formask, Netmask(entry.second), order);
3e61e7f7 235 }
236 ++order;
237 }
238 }
239 }
240 catch(std::exception& e) {
241 theL()<<Logger::Error<<"Error in addSortList: "<<e.what()<<endl;
242 }
243 });
2ac8ae89 244
245 Lua.writeFunction("addDS", [&lci](const std::string& who, const std::string& what) {
e48c6b8a 246 warnIfDNSSECDisabled("Warning: adding Trust Anchor for DNSSEC (addDS), but dnssec is set to 'off'!");
52ad9eea 247 DNSName zone(who);
52ad9eea 248 auto ds = unique_ptr<DSRecordContent>(dynamic_cast<DSRecordContent*>(DSRecordContent::make(what)));
64a4a928 249 lci.dsAnchors[zone].insert(*ds);
52ad9eea 250 });
2ac8ae89 251
252 Lua.writeFunction("clearDS", [&lci](boost::optional<string> who) {
e48c6b8a 253 warnIfDNSSECDisabled("Warning: removing Trust Anchor for DNSSEC (clearDS), but dnssec is set to 'off'!");
2ac8ae89 254 if(who)
255 lci.dsAnchors.erase(DNSName(*who));
256 else
257 lci.dsAnchors.clear();
258 });
259
1bd49828 260 Lua.writeFunction("addNTA", [&lci](const std::string& who, const boost::optional<std::string> why) {
e48c6b8a 261 warnIfDNSSECDisabled("Warning: adding Negative Trust Anchor for DNSSEC (addNTA), but dnssec is set to 'off'!");
1bd49828
PL
262 if(why)
263 lci.negAnchors[DNSName(who)] = static_cast<string>(*why);
264 else
265 lci.negAnchors[DNSName(who)] = "";
266 });
267
268 Lua.writeFunction("clearNTA", [&lci](boost::optional<string> who) {
e48c6b8a 269 warnIfDNSSECDisabled("Warning: removing Negative Trust Anchor for DNSSEC (clearNTA), but dnssec is set to 'off'!");
1bd49828
PL
270 if(who)
271 lci.negAnchors.erase(DNSName(*who));
272 else
273 lci.negAnchors.clear();
274 });
275
aa7929a3 276#if HAVE_PROTOBUF
a4241908 277 Lua.writeFunction("protobufServer", [&lci, checkOnly](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) {
aa7929a3
RG
278 try {
279 ComboAddress server(server_);
280 if (!lci.protobufServer) {
a4241908
RG
281 if (!checkOnly) {
282 lci.protobufServer = std::make_shared<RemoteLogger>(server, timeout ? *timeout : 2, maxQueuedEntries ? *maxQueuedEntries : 100, reconnectWaitTime ? *reconnectWaitTime : 1, asyncConnect ? *asyncConnect : false);
283 }
284
e1c8a4bb
RG
285 if (maskV4) {
286 lci.protobufMaskV4 = *maskV4;
287 }
288 if (maskV6) {
289 lci.protobufMaskV6 = *maskV6;
290 }
b790ef3d
RG
291 if (taggedOnly) {
292 lci.protobufTaggedOnly = *taggedOnly;
293 }
aa7929a3
RG
294 }
295 else {
296 theL()<<Logger::Error<<"Only one protobuf server can be configured, we already have "<<lci.protobufServer->toString()<<endl;
297 }
298 }
4898a348
RG
299 catch(std::exception& e) {
300 theL()<<Logger::Error<<"Error while starting protobuf logger to '"<<server_<<": "<<e.what()<<endl;
301 }
302 catch(PDNSException& e) {
303 theL()<<Logger::Error<<"Error while starting protobuf logger to '"<<server_<<": "<<e.reason<<endl;
304 }
305 });
306
a79b00a6 307 Lua.writeFunction("outgoingProtobufServer", [&lci, checkOnly](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) {
4898a348
RG
308 try {
309 ComboAddress server(server_);
310 if (!lci.outgoingProtobufServer) {
a79b00a6
RG
311 if (!checkOnly) {
312 lci.outgoingProtobufServer = std::make_shared<RemoteLogger>(server, timeout ? *timeout : 2, maxQueuedEntries ? *maxQueuedEntries : 100, reconnectWaitTime ? *reconnectWaitTime : 1, asyncConnect ? *asyncConnect : false);
313 }
4898a348
RG
314 }
315 else {
316 theL()<<Logger::Error<<"Only one protobuf server can be configured, we already have "<<lci.protobufServer->toString()<<endl;
317 }
318 }
aa7929a3
RG
319 catch(std::exception& e) {
320 theL()<<Logger::Error<<"Error while starting protobuf logger to '"<<server_<<": "<<e.what()<<endl;
321 }
322 catch(PDNSException& e) {
323 theL()<<Logger::Error<<"Error while starting protobuf logger to '"<<server_<<": "<<e.reason<<endl;
324 }
325 });
326#endif
327
ad42489c 328 try {
329 Lua.executeCode(ifs);
330 g_luaconfs.setState(lci);
331 }
2ac8ae89 332 catch(const LuaContext::ExecutionErrorException& e) {
333 theL()<<Logger::Error<<"Unable to load Lua script from '"+fname+"': ";
334 try {
335 std::rethrow_if_nested(e);
dd079764
RG
336 } catch(const std::exception& exp) {
337 // exp is the exception that was thrown from inside the lambda
338 theL() << exp.what() << std::endl;
2ac8ae89 339 }
dd079764
RG
340 catch(const PDNSException& exp) {
341 // exp is the exception that was thrown from inside the lambda
342 theL() << exp.reason << std::endl;
2ac8ae89 343 }
344 throw;
345
346 }
ad42489c 347 catch(std::exception& err) {
348 theL()<<Logger::Error<<"Unable to load Lua script from '"+fname+"': "<<err.what()<<endl;
2ac8ae89 349 throw;
ad42489c 350 }
351
3e61e7f7 352}
6a99b9a4 353