2 #include "ext/luawrapper/include/LuaContext.hpp"
6 #include "namespaces.hh"
8 #include "rec-lua-conf.hh"
10 #include "filterpo.hh"
12 #include "rpzloader.hh"
14 #include "remote_logger.hh"
15 #include "validate.hh"
16 #include "validate-recursor.hh"
17 #include "root-dnssec.hh"
19 GlobalStateHolder
<LuaConfigItems
> g_luaconfs
;
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.
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.
28 If someone loads an empty Lua file, the default LuaConfigItems struct MUST MAKE SENSE.
30 To make this easy on you, here is a LuaConfigItems constructor where you
31 can set sane defaults:
34 LuaConfigItems::LuaConfigItems()
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
);
43 /* DID YOU READ THE STORY ABOVE? */
46 typename
C::value_type::second_type
constGet(const C
& c
, const std::string
& name
)
48 auto iter
= c
.find(name
);
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
)
57 if(have
.count("policyName")) {
58 polName
= boost::get
<std::string
>(constGet(have
, "policyName"));
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
) {
66 DNSRecordContent::mastermake(QType::CNAME
, 1,
67 boost::get
<string
>(constGet(have
,"defcontent"))
70 if(have
.count("defttl"))
71 defpol
->d_ttl
= static_cast<int32_t>(boost::get
<uint32_t>(constGet(have
, "defttl")));
73 defpol
->d_ttl
= -1; // get it from the zone
76 if(have
.count("maxTTL")) {
77 maxTTL
= boost::get
<uint32_t>(constGet(have
, "maxTTL"));
79 if(have
.count("zoneSizeHint")) {
80 zoneSizeHint
= static_cast<size_t>(boost::get
<uint32_t>(constGet(have
, "zoneSizeHint")));
84 void loadRecursorLuaConfig(const std::string
& fname
, bool checkOnly
)
93 throw PDNSException("Cannot open file '"+fname
+"': "+strerror(errno
));
95 Lua
.writeFunction("clearSortlist", [&lci
]() { lci
.sortlist
.clear(); });
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"}}
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
}
110 Lua
.writeVariable("Policy", pmap
);
112 Lua
.writeFunction("rpzFile", [&lci
](const string
& filename
, const boost::optional
<std::unordered_map
<string
,boost::variant
<uint32_t, string
>>>& options
) {
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();
119 auto& have
= *options
;
120 size_t zoneSizeHint
= 0;
121 parseRPZParameters(have
, polName
, defpol
, maxTTL
, zoneSizeHint
);
122 if (zoneSizeHint
> 0) {
123 zone
->reserve(zoneSizeHint
);
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
;
132 catch(const std::exception
& e
) {
133 g_log
<<Logger::Error
<<"Unable to load RPZ zone from '"<<filename
<<"': "<<e
.what()<<endl
;
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
) {
139 boost::optional
<DNSFilterEngine::Policy
> defpol
;
140 std::shared_ptr
<DNSFilterEngine::Zone
> zone
= std::make_shared
<DNSFilterEngine::Zone
>();
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);
151 std::string
polName(zoneName
);
153 auto& have
= *options
;
154 size_t zoneSizeHint
= 0;
155 parseRPZParameters(have
, polName
, defpol
, maxTTL
, zoneSizeHint
);
156 if (zoneSizeHint
> 0) {
157 zone
->reserve(zoneSizeHint
);
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");
165 if(have
.count("refresh")) {
166 refresh
= boost::get
<uint32_t>(constGet(have
,"refresh"));
168 if(have
.count("maxReceivedMBytes")) {
169 maxReceivedXFRMBytes
= static_cast<size_t>(boost::get
<uint32_t>(constGet(have
,"maxReceivedMBytes")));
171 if(have
.count("localAddress")) {
172 localAddress
= ComboAddress(boost::get
<string
>(constGet(have
,"localAddress")));
174 if(have
.count("axfrTimeout")) {
175 axfrTimeout
= static_cast<uint16_t>(boost::get
<uint32_t>(constGet(have
, "axfrTimeout")));
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()+").");
183 zone
->setDomain(DNSName(zoneName
));
184 zone
->setName(polName
);
185 zone
->setRefresh(refresh
);
186 zoneIdx
= lci
.dfe
.addZone(zone
);
188 catch(const std::exception
& e
) {
189 g_log
<<Logger::Error
<<"Problem configuring 'rpzMaster': "<<e
.what()<<endl
;
190 exit(1); // FIXME proper exit code?
192 catch(const PDNSException
& e
) {
193 g_log
<<Logger::Error
<<"Problem configuring 'rpzMaster': "<<e
.reason
<<endl
;
194 exit(1); // FIXME proper exit code?
199 std::thread
t(RPZIXFRTracker
, master
, defpol
, maxTTL
, zoneIdx
, tt
, maxReceivedXFRMBytes
* 1024 * 1024, localAddress
, zone
, axfrTimeout
);
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?
207 catch(const PDNSException
& e
) {
208 g_log
<<Logger::Error
<<"Problem starting RPZIXFRTracker thread: "<<e
.reason
<<endl
;
209 exit(1); // FIXME proper exit code?
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_
)
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
);
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
);
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
);
240 catch(std::exception
& e
) {
241 g_log
<<Logger::Error
<<"Error in addSortList: "<<e
.what()<<endl
;
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'!");
248 auto ds
= unique_ptr
<DSRecordContent
>(dynamic_cast<DSRecordContent
*>(DSRecordContent::make(what
)));
249 lci
.dsAnchors
[zone
].insert(*ds
);
252 Lua
.writeFunction("clearDS", [&lci
](boost::optional
<string
> who
) {
253 warnIfDNSSECDisabled("Warning: removing Trust Anchor for DNSSEC (clearDS), but dnssec is set to 'off'!");
255 lci
.dsAnchors
.erase(DNSName(*who
));
257 lci
.dsAnchors
.clear();
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'!");
263 lci
.negAnchors
[DNSName(who
)] = static_cast<string
>(*why
);
265 lci
.negAnchors
[DNSName(who
)] = "";
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'!");
271 lci
.negAnchors
.erase(DNSName(*who
));
273 lci
.negAnchors
.clear();
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
) {
279 ComboAddress
server(server_
);
280 if (!lci
.protobufServer
) {
282 lci
.protobufServer
= std::make_shared
<RemoteLogger
>(server
, timeout
? *timeout
: 2, maxQueuedEntries
? *maxQueuedEntries
: 100, reconnectWaitTime
? *reconnectWaitTime
: 1, asyncConnect
? *asyncConnect
: false);
286 lci
.protobufMaskV4
= *maskV4
;
289 lci
.protobufMaskV6
= *maskV6
;
292 lci
.protobufTaggedOnly
= *taggedOnly
;
296 g_log
<<Logger::Error
<<"Only one protobuf server can be configured, we already have "<<lci
.protobufServer
->toString()<<endl
;
299 catch(std::exception
& e
) {
300 g_log
<<Logger::Error
<<"Error while starting protobuf logger to '"<<server_
<<": "<<e
.what()<<endl
;
302 catch(PDNSException
& e
) {
303 g_log
<<Logger::Error
<<"Error while starting protobuf logger to '"<<server_
<<": "<<e
.reason
<<endl
;
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
) {
309 ComboAddress
server(server_
);
310 if (!lci
.outgoingProtobufServer
) {
312 lci
.outgoingProtobufServer
= std::make_shared
<RemoteLogger
>(server
, timeout
? *timeout
: 2, maxQueuedEntries
? *maxQueuedEntries
: 100, reconnectWaitTime
? *reconnectWaitTime
: 1, asyncConnect
? *asyncConnect
: false);
316 g_log
<<Logger::Error
<<"Only one protobuf server can be configured, we already have "<<lci
.protobufServer
->toString()<<endl
;
319 catch(std::exception
& e
) {
320 g_log
<<Logger::Error
<<"Error while starting protobuf logger to '"<<server_
<<": "<<e
.what()<<endl
;
322 catch(PDNSException
& e
) {
323 g_log
<<Logger::Error
<<"Error while starting protobuf logger to '"<<server_
<<": "<<e
.reason
<<endl
;
329 Lua
.executeCode(ifs
);
330 g_luaconfs
.setState(lci
);
332 catch(const LuaContext::ExecutionErrorException
& e
) {
333 g_log
<<Logger::Error
<<"Unable to load Lua script from '"+fname
+"': ";
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
;
340 catch(const PDNSException
& exp
) {
341 // exp is the exception that was thrown from inside the lambda
342 g_log
<< exp
.reason
<< std::endl
;
347 catch(std::exception
& err
) {
348 g_log
<<Logger::Error
<<"Unable to load Lua script from '"+fname
+"': "<<err
.what()<<endl
;