]>
Commit | Line | Data |
---|---|---|
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 | 19 | GlobalStateHolder<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 | ||
34 | LuaConfigItems::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 | ||
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 | ||
6a99b9a4 | 54 | |
8f618901 RG |
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= | |
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 | 84 | void 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 |