]>
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 | { | |
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 | ||
44 | template <typename C> | |
45 | typename 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 |
54 | 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) |
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 | 85 | void 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 |