]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/rec-lua-conf.cc
Logging: have a global g_log
[thirdparty/pdns.git] / pdns / 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 "rec-lua-conf.hh"
9 #include "sortlist.hh"
10 #include "filterpo.hh"
11 #include "syncres.hh"
12 #include "rpzloader.hh"
13 #include "base64.hh"
14 #include "remote_logger.hh"
15 #include "validate.hh"
16 #include "validate-recursor.hh"
17 #include "root-dnssec.hh"
18
19 GlobalStateHolder<LuaConfigItems> g_luaconfs;
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
34 LuaConfigItems::LuaConfigItems()
35 {
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);
40 }
41 }
42
43 /* DID YOU READ THE STORY ABOVE? */
44
45 template <typename C>
46 typename 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
54
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)
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=
66 DNSRecordContent::mastermake(QType::CNAME, 1,
67 boost::get<string>(constGet(have,"defcontent"))
68 );
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
84 void loadRecursorLuaConfig(const std::string& fname, bool checkOnly)
85 {
86 LuaConfigItems lci;
87
88 LuaContext Lua;
89 if(fname.empty())
90 return;
91 ifstream ifs(fname);
92 if(!ifs)
93 throw PDNSException("Cannot open file '"+fname+"': "+strerror(errno));
94
95 Lua.writeFunction("clearSortlist", [&lci]() { lci.sortlist.clear(); });
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
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
112 Lua.writeFunction("rpzFile", [&lci](const string& filename, const boost::optional<std::unordered_map<string,boost::variant<uint32_t, string>>>& options) {
113 try {
114 boost::optional<DNSFilterEngine::Policy> defpol;
115 std::string polName("rpzFile");
116 std::shared_ptr<DNSFilterEngine::Zone> zone = std::make_shared<DNSFilterEngine::Zone>();
117 uint32_t maxTTL = std::numeric_limits<uint32_t>::max();
118 if(options) {
119 auto& have = *options;
120 size_t zoneSizeHint = 0;
121 parseRPZParameters(have, polName, defpol, maxTTL, zoneSizeHint);
122 if (zoneSizeHint > 0) {
123 zone->reserve(zoneSizeHint);
124 }
125 }
126 g_log<<Logger::Warning<<"Loading RPZ from file '"<<filename<<"'"<<endl;
127 zone->setName(polName);
128 loadRPZFromFile(filename, zone, defpol, maxTTL);
129 lci.dfe.addZone(zone);
130 g_log<<Logger::Warning<<"Done loading RPZ from file '"<<filename<<"'"<<endl;
131 }
132 catch(const std::exception& e) {
133 g_log<<Logger::Error<<"Unable to load RPZ zone from '"<<filename<<"': "<<e.what()<<endl;
134 }
135 });
136
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) {
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;
144 uint16_t axfrTimeout = 20;
145 uint32_t maxTTL = std::numeric_limits<uint32_t>::max();
146 ComboAddress localAddress;
147 ComboAddress master(master_, 53);
148 size_t zoneIdx;
149
150 try {
151 std::string polName(zoneName);
152 if(options) {
153 auto& have = *options;
154 size_t zoneSizeHint = 0;
155 parseRPZParameters(have, polName, defpol, maxTTL, zoneSizeHint);
156 if (zoneSizeHint > 0) {
157 zone->reserve(zoneSizeHint);
158 }
159 if(have.count("tsigname")) {
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")) {
166 refresh = boost::get<uint32_t>(constGet(have,"refresh"));
167 }
168 if(have.count("maxReceivedMBytes")) {
169 maxReceivedXFRMBytes = static_cast<size_t>(boost::get<uint32_t>(constGet(have,"maxReceivedMBytes")));
170 }
171 if(have.count("localAddress")) {
172 localAddress = ComboAddress(boost::get<string>(constGet(have,"localAddress")));
173 }
174 if(have.count("axfrTimeout")) {
175 axfrTimeout = static_cast<uint16_t>(boost::get<uint32_t>(constGet(have, "axfrTimeout")));
176 }
177 }
178 if (localAddress != ComboAddress() && localAddress.sin4.sin_family != master.sin4.sin_family) {
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()+").");
181 }
182
183 zone->setDomain(DNSName(zoneName));
184 zone->setName(polName);
185 zone->setRefresh(refresh);
186 zoneIdx = lci.dfe.addZone(zone);
187 }
188 catch(const std::exception& e) {
189 g_log<<Logger::Error<<"Problem configuring 'rpzMaster': "<<e.what()<<endl;
190 exit(1); // FIXME proper exit code?
191 }
192 catch(const PDNSException& e) {
193 g_log<<Logger::Error<<"Problem configuring 'rpzMaster': "<<e.reason<<endl;
194 exit(1); // FIXME proper exit code?
195 }
196
197 try {
198 if (!checkOnly) {
199 std::thread t(RPZIXFRTracker, master, defpol, maxTTL, zoneIdx, tt, maxReceivedXFRMBytes * 1024 * 1024, localAddress, zone, axfrTimeout);
200 t.detach();
201 }
202 }
203 catch(const std::exception& e) {
204 g_log<<Logger::Error<<"Problem starting RPZIXFRTracker thread: "<<e.what()<<endl;
205 exit(1); // FIXME proper exit code?
206 }
207 catch(const PDNSException& e) {
208 g_log<<Logger::Error<<"Problem starting RPZIXFRTracker thread: "<<e.reason<<endl;
209 exit(1); // FIXME proper exit code?
210 }
211 });
212
213 typedef vector<pair<int,boost::variant<string, vector<pair<int, string> > > > > argvec_t;
214 Lua.writeFunction("addSortList",
215 [&lci](const std::string& formask_,
216 const boost::variant<string, argvec_t>& masks,
217 boost::optional<int> order_)
218 {
219 try {
220 Netmask formask(formask_);
221 int order = order_ ? (*order_) : lci.sortlist.getMaxOrder(formask)+1;
222 if(auto str = boost::get<string>(&masks))
223 lci.sortlist.addEntry(formask, Netmask(*str), order);
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)) {
229 lci.sortlist.addEntry(formask, Netmask(*s), order);
230 }
231 else {
232 const auto& v =boost::get<vector<pair<int, string> > >(e.second);
233 for(const auto& entry : v)
234 lci.sortlist.addEntry(formask, Netmask(entry.second), order);
235 }
236 ++order;
237 }
238 }
239 }
240 catch(std::exception& e) {
241 g_log<<Logger::Error<<"Error in addSortList: "<<e.what()<<endl;
242 }
243 });
244
245 Lua.writeFunction("addDS", [&lci](const std::string& who, const std::string& what) {
246 warnIfDNSSECDisabled("Warning: adding Trust Anchor for DNSSEC (addDS), but dnssec is set to 'off'!");
247 DNSName zone(who);
248 auto ds = unique_ptr<DSRecordContent>(dynamic_cast<DSRecordContent*>(DSRecordContent::make(what)));
249 lci.dsAnchors[zone].insert(*ds);
250 });
251
252 Lua.writeFunction("clearDS", [&lci](boost::optional<string> who) {
253 warnIfDNSSECDisabled("Warning: removing Trust Anchor for DNSSEC (clearDS), but dnssec is set to 'off'!");
254 if(who)
255 lci.dsAnchors.erase(DNSName(*who));
256 else
257 lci.dsAnchors.clear();
258 });
259
260 Lua.writeFunction("addNTA", [&lci](const std::string& who, const boost::optional<std::string> why) {
261 warnIfDNSSECDisabled("Warning: adding Negative Trust Anchor for DNSSEC (addNTA), but dnssec is set to 'off'!");
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) {
269 warnIfDNSSECDisabled("Warning: removing Negative Trust Anchor for DNSSEC (clearNTA), but dnssec is set to 'off'!");
270 if(who)
271 lci.negAnchors.erase(DNSName(*who));
272 else
273 lci.negAnchors.clear();
274 });
275
276 #if HAVE_PROTOBUF
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) {
278 try {
279 ComboAddress server(server_);
280 if (!lci.protobufServer) {
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
285 if (maskV4) {
286 lci.protobufMaskV4 = *maskV4;
287 }
288 if (maskV6) {
289 lci.protobufMaskV6 = *maskV6;
290 }
291 if (taggedOnly) {
292 lci.protobufTaggedOnly = *taggedOnly;
293 }
294 }
295 else {
296 g_log<<Logger::Error<<"Only one protobuf server can be configured, we already have "<<lci.protobufServer->toString()<<endl;
297 }
298 }
299 catch(std::exception& e) {
300 g_log<<Logger::Error<<"Error while starting protobuf logger to '"<<server_<<": "<<e.what()<<endl;
301 }
302 catch(PDNSException& e) {
303 g_log<<Logger::Error<<"Error while starting protobuf logger to '"<<server_<<": "<<e.reason<<endl;
304 }
305 });
306
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) {
308 try {
309 ComboAddress server(server_);
310 if (!lci.outgoingProtobufServer) {
311 if (!checkOnly) {
312 lci.outgoingProtobufServer = std::make_shared<RemoteLogger>(server, timeout ? *timeout : 2, maxQueuedEntries ? *maxQueuedEntries : 100, reconnectWaitTime ? *reconnectWaitTime : 1, asyncConnect ? *asyncConnect : false);
313 }
314 }
315 else {
316 g_log<<Logger::Error<<"Only one protobuf server can be configured, we already have "<<lci.protobufServer->toString()<<endl;
317 }
318 }
319 catch(std::exception& e) {
320 g_log<<Logger::Error<<"Error while starting protobuf logger to '"<<server_<<": "<<e.what()<<endl;
321 }
322 catch(PDNSException& e) {
323 g_log<<Logger::Error<<"Error while starting protobuf logger to '"<<server_<<": "<<e.reason<<endl;
324 }
325 });
326 #endif
327
328 try {
329 Lua.executeCode(ifs);
330 g_luaconfs.setState(lci);
331 }
332 catch(const LuaContext::ExecutionErrorException& e) {
333 g_log<<Logger::Error<<"Unable to load Lua script from '"+fname+"': ";
334 try {
335 std::rethrow_if_nested(e);
336 } catch(const std::exception& exp) {
337 // exp is the exception that was thrown from inside the lambda
338 g_log << exp.what() << std::endl;
339 }
340 catch(const PDNSException& exp) {
341 // exp is the exception that was thrown from inside the lambda
342 g_log << exp.reason << std::endl;
343 }
344 throw;
345
346 }
347 catch(std::exception& err) {
348 g_log<<Logger::Error<<"Unable to load Lua script from '"+fname+"': "<<err.what()<<endl;
349 throw;
350 }
351
352 }
353