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