d_lw->writeFunction("getregisteredname", [](const DNSName &dname) {
return getRegisteredName(dname);
});
+
+ d_lw->registerMember<const DNSName (PolicyEvent::*)>("qname", [](const PolicyEvent& event) -> const DNSName& { return event.qname; }, [](PolicyEvent& event, const DNSName& newName) { (void) newName; });
+ d_lw->registerMember<uint16_t (PolicyEvent::*)>("qtype", [](const PolicyEvent& event) -> uint16_t { return event.qtype.getCode(); }, [](PolicyEvent& event, uint16_t newType) { (void) newType; });
+ d_lw->registerMember<bool (PolicyEvent::*)>("isTcp", [](const PolicyEvent& event) -> bool { return event.isTcp; }, [](PolicyEvent& event, bool newTcp) { (void) newTcp; });
+ d_lw->registerMember<const ComboAddress (PolicyEvent::*)>("remote", [](const PolicyEvent& event) -> const ComboAddress& { return event.remote; }, [](PolicyEvent& event, const ComboAddress& newRemote) { (void) newRemote; });
+ d_lw->registerMember("appliedPolicy", &PolicyEvent::appliedPolicy);
+ d_lw->registerFunction<void(PolicyEvent::*)(const std::string&)>("addPolicyTag", [](PolicyEvent& event, const std::string& tag) { if (event.policyTags) { event.policyTags->insert(tag); } });
+ d_lw->registerFunction<void(PolicyEvent::*)(const std::vector<std::pair<int, std::string> >&)>("setPolicyTags", [](PolicyEvent& event, const std::vector<std::pair<int, std::string> >& tags) {
+ if (event.policyTags) {
+ event.policyTags->clear();
+ event.policyTags->reserve(tags.size());
+ for (const auto& tag : tags) {
+ event.policyTags->insert(tag.second);
+ }
+ }
+ });
+ d_lw->registerFunction<std::vector<std::pair<int, std::string> >(PolicyEvent::*)()>("getPolicyTags", [](const PolicyEvent& event) {
+ std::vector<std::pair<int, std::string> > ret;
+ if (event.policyTags) {
+ int count = 1;
+ ret.reserve(event.policyTags->size());
+ for (const auto& tag : *event.policyTags) {
+ ret.push_back({count++, tag});
+ }
+ }
+ return ret;
+ });
+ d_lw->registerFunction<void(PolicyEvent::*)(const std::string&)>("discardPolicy", [](PolicyEvent& event, const std::string& policy) {
+ if (event.discardedPolicies) {
+ (*event.discardedPolicies)[policy] = true;
+ }
+ });
}
void RecursorLua4::postLoad() {
d_ipfilter = d_lw->readVariable<boost::optional<ipfilter_t>>("ipfilter").get_value_or(0);
d_gettag = d_lw->readVariable<boost::optional<gettag_t>>("gettag").get_value_or(0);
d_gettag_ffi = d_lw->readVariable<boost::optional<gettag_ffi_t>>("gettag_ffi").get_value_or(0);
+
+ d_policyHitEventFilter = d_lw->readVariable<boost::optional<policyEventFilter_t>>("policyEventFilter").get_value_or(0);
}
void RecursorLua4::getFeatures(Features & features) {
return false; // don't block
}
+bool RecursorLua4::policyHitEventFilter(const ComboAddress& remote, const DNSName& qname, const QType& qtype, bool tcp, DNSFilterEngine::Policy& policy, std::unordered_set<std::string>& tags, std::unordered_map<std::string,bool>& dicardedPolicies) const
+{
+ if (!d_policyHitEventFilter) {
+ return false;
+ }
+
+ PolicyEvent event(remote, qname, qtype, tcp);
+ event.appliedPolicy = &policy;
+ event.policyTags = &tags;
+ event.discardedPolicies = &dicardedPolicies;
+
+ if (d_policyHitEventFilter(event)) {
+ return true;
+ }
+ else {
+ return false;
+ }
+}
+
unsigned int RecursorLua4::gettag(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& qname, uint16_t qtype, std::unordered_set<std::string>* policyTags, LuaContext::LuaObject& data, const EDNSOptionViewMap& ednsOptions, bool tcp, std::string& requestorId, std::string& deviceId, std::string& deviceName, std::string& routingTag, const std::vector<ProxyProtocolValue>& proxyProtocolValues) const
{
if(d_gettag) {
DNSName followupName;
};
+ struct PolicyEvent
+ {
+ PolicyEvent(const ComboAddress& rem, const DNSName& name, const QType& type, bool tcp): qname(name), qtype(type), remote(rem), isTcp(tcp)
+ {
+ }
+ const DNSName& qname;
+ const QType qtype;
+ const ComboAddress& remote;
+ const bool isTcp;
+ DNSFilterEngine::Policy* appliedPolicy{nullptr};
+ std::unordered_set<std::string>* policyTags{nullptr};
+ std::unordered_map<std::string,bool>* discardedPolicies{nullptr};
+ };
+
unsigned int gettag(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& qname, uint16_t qtype, std::unordered_set<std::string>* policyTags, LuaContext::LuaObject& data, const EDNSOptionViewMap&, bool tcp, std::string& requestorId, std::string& deviceId, std::string& deviceName, std::string& routingTag, const std::vector<ProxyProtocolValue>& proxyProtocolValues) const;
unsigned int gettag_ffi(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& qname, uint16_t qtype, std::unordered_set<std::string>* policyTags, std::vector<DNSRecord>& records, LuaContext::LuaObject& data, const EDNSOptionViewMap& ednsOptions, bool tcp, const std::vector<ProxyProtocolValue>& proxyProtocolValues, std::string& requestorId, std::string& deviceId, std::string& deviceName, std::string& routingTag, boost::optional<int>& rcode, uint32_t& ttlCap, bool& variable, bool& logQuery, bool& logResponse, bool& followCNAMERecords) const;
bool preoutquery(const ComboAddress& ns, const ComboAddress& requestor, const DNSName& query, const QType& qtype, bool isTcp, vector<DNSRecord>& res, int& ret) const;
bool ipfilter(const ComboAddress& remote, const ComboAddress& local, const struct dnsheader&) const;
+ bool policyHitEventFilter(const ComboAddress& remote, const DNSName& qname, const QType& qtype, bool tcp, DNSFilterEngine::Policy& policy, std::unordered_set<std::string>& tags, std::unordered_map<std::string,bool>& dicardedPolicies) const;
+
bool needDQ() const
{
return (d_prerpz ||
bool genhook(const luacall_t& func, DNSQuestion& dq, int& ret) const;
typedef std::function<bool(ComboAddress,ComboAddress, struct dnsheader)> ipfilter_t;
ipfilter_t d_ipfilter;
+ typedef std::function<bool(PolicyEvent&)> policyEventFilter_t;
+ policyEventFilter_t d_policyHitEventFilter;
};
thread_local std::unique_ptr<MT_t> MT; // the big MTasker
std::unique_ptr<MemRecursorCache> s_RC;
-
thread_local std::unique_ptr<RecursorPacketCache> t_packetCache;
thread_local FDMultiplexer* t_fdm{nullptr};
thread_local std::unique_ptr<addrringbuf_t> t_remotes, t_servfailremotes, t_largeanswerremotes, t_bogusremotes;
if (luaconfsLocal->dfe.getClientPolicy(dc->d_source, sr.d_discardedPolicies, appliedPolicy)) {
mergePolicyTags(dc->d_policyTags, appliedPolicy.getTags());
}
- }
+ }
/* If we already have an answer generated from gettag_ffi, let's see if the filtering policies
should be applied to it */
goto haveAnswer;
}
}
-
+
// if there is a RecursorLua active, and it 'took' the query in preResolve, we don't launch beginResolve
if (!t_pdl || !t_pdl->preresolve(dq, res)) {
}
sr.setWantsRPZ(wantsRPZ);
+
if (wantsRPZ && appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) {
- auto policyResult = handlePolicyHit(appliedPolicy, dc, sr, res, ret, pw, false);
- if (policyResult == PolicyResult::HaveAnswer) {
- goto haveAnswer;
+
+ if (t_pdl && t_pdl->policyHitEventFilter(dc->d_remote, dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), dc->d_tcp, appliedPolicy, dc->d_policyTags, sr.d_discardedPolicies)) {
+ /* reset to no match */
+ appliedPolicy = DNSFilterEngine::Policy();
}
- else if (policyResult == PolicyResult::Drop) {
- return;
+ else {
+ auto policyResult = handlePolicyHit(appliedPolicy, dc, sr, res, ret, pw, false);
+ if (policyResult == PolicyResult::HaveAnswer) {
+ goto haveAnswer;
+ }
+ else if (policyResult == PolicyResult::Drop) {
+ return;
+ }
}
}
- // Query got not handled for QNAME Policy reasons, now actually go out to find an answer
+ // Query did not get handled for Client IP or QNAME Policy reasons, now actually go out to find an answer
try {
sr.d_appliedPolicy = appliedPolicy;
sr.d_policyTags = std::move(dc->d_policyTags);
shouldNotValidate = sr.wasOutOfBand();
}
catch (const ImmediateQueryDropException& e) {
-#warning We need to export a protobuf (and NOD lookup?) message if requested!
+ // XXX We need to export a protobuf message (and do a NOD lookup) if requested!
g_stats.policyDrops++;
g_log<<Logger::Debug<<"Dropping query because of a filtering policy "<<makeLoginfo(dc)<<endl;
return;
return false;
}
+bool RecursorLua4::policyHitEventFilter(const ComboAddress& remote, const DNSName& qname, const QType& qtype, bool tcp, DNSFilterEngine::Policy& policy, std::unordered_set<std::string>& tags, std::unordered_map<std::string,bool>& dicardedPolicies) const
+{
+ return false;
+}
+
int asyncresolve(const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, const std::shared_ptr<std::vector<std::unique_ptr<RemoteLogger>>>& outgoingLoggers, const std::shared_ptr<std::vector<std::unique_ptr<FrameStreamLogger>>>& fstrmLoggers, const std::set<uint16_t>& exportTypes, LWResult* res, bool* chained)
{
return 0;
void SyncRes::handlePolicyHit(const std::string& prefix, const DNSName& qname, const QType& qtype, std::vector<DNSRecord>& ret, bool& done, int& rcode, unsigned int depth)
{
+ if (d_pdl && d_pdl->policyHitEventFilter(d_requestor, qname, qtype, d_queryReceivedOverTCP, d_appliedPolicy, d_policyTags, d_discardedPolicies)) {
+ /* reset to no match */
+ d_appliedPolicy = DNSFilterEngine::Policy();
+ return;
+ }
+
/* don't account truncate actions for TCP queries, since they are not applied */
if (d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::Truncate || !d_queryReceivedOverTCP) {
++g_stats.policyResults[d_appliedPolicy.d_kind];
}
- cerr<<"Handling policy hit for "<<qname<<" of type "<<(int)d_appliedPolicy.d_kind<<endl;
switch (d_appliedPolicy.d_kind) {
case DNSFilterEngine::PolicyKind::NoAction:
case DNSFilterEngine::PolicyKind::Custom:
{
- cerr<<"custom"<<endl;
if (rpzHitShouldReplaceContent(qname, qtype, ret)) {
- cerr<<"clearing"<<endl;
ret.clear();
}
if (dr.d_name == qname && dr.d_type == QType::CNAME && qtype != QType::CNAME) {
if (auto content = getRR<CNAMERecordContent>(dr)) {
-#warning FIXME shall we stop RPZ processing from here?
- // https://tools.ietf.org/html/draft-vixie-dnsop-dns-rpz-00#section-6
vState newTargetState = vState::Indeterminate;
handleNewTarget(prefix, qname, content->getTarget(), qtype.getCode(), ret, rcode, depth, {}, newTargetState);
}
if (match) {
mergePolicyTags(d_policyTags, d_appliedPolicy.getTags());
if (d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) { // client query needs an RPZ response
- LOG("however "<<nameserver<<" was blocked by RPZ policy '"<<d_appliedPolicy.getName()<<"'"<<endl);
- throw PolicyHitException();
+ if (d_pdl && d_pdl->policyHitEventFilter(d_requestor, qname, qtype, d_queryReceivedOverTCP, d_appliedPolicy, d_policyTags, d_discardedPolicies)) {
+ /* reset to no match */
+ d_appliedPolicy = DNSFilterEngine::Policy();
+ }
+ else {
+ LOG("however "<<nameserver<<" was blocked by RPZ policy '"<<d_appliedPolicy.getName()<<"'"<<endl);
+ throw PolicyHitException();
+ }
}
}
}
if (nameserversBlockedByRPZ(luaconfsLocal->dfe, nameservers)) {
/* RPZ hit */
- throw PolicyHitException();
+ if (d_pdl && d_pdl->policyHitEventFilter(d_requestor, qname, qtype, d_queryReceivedOverTCP, d_appliedPolicy, d_policyTags, d_discardedPolicies)) {
+ /* reset to no match */
+ d_appliedPolicy = DNSFilterEngine::Policy();
+ }
+ else {
+ throw PolicyHitException();
+ }
}
LOG(endl);
LOG(endl);
if (hitPolicy) { //implies d_wantsRPZ
/* RPZ hit */
- throw PolicyHitException();
+ if (d_pdl && d_pdl->policyHitEventFilter(d_requestor, qname, qtype, d_queryReceivedOverTCP, d_appliedPolicy, d_policyTags, d_discardedPolicies)) {
+ /* reset to no match */
+ d_appliedPolicy = DNSFilterEngine::Policy();
+ }
+ else {
+ throw PolicyHitException();
+ }
}
}
if dq.qname:equal("android.marvin.example.net") then
dq.wantsRPZ = false -- disable RPZ
end
- if dq.appliedPolicy.policyKind == pdns.policykinds.Custom then
- if dq.qname:equal("www3.example.net") then
- dq.appliedPolicy.policyCustom = "www2.example.net"
+ return false
+end
+
+function policyEventFilter(event)
+ if event.appliedPolicy.policyKind == pdns.policykinds.Custom then
+ if event.qname:equal("www3.example.net") then
+ event.appliedPolicy.policyCustom = "www2.example.net"
+ return false
end
end
return false
-0 www.trillian.example.net. IN CNAME 3600 www2.arthur.example.net.
-0 www2.arthur.example.net. IN A 3600 192.0.2.6
+0 www.trillian.example.net. IN CNAME 3600 www3.arthur.example.net.
+0 www3.arthur.example.net. IN A 3600 192.0.2.6
Rcode: 0 (No Error), RD: 1, QR: 1, TC: 0, AA: 0, opcode: 0
Reply to question for qname='www.trillian.example.net.', qtype=A