From: Pieter Lexis Date: Wed, 27 Jul 2016 20:34:08 +0000 (+0200) Subject: RPZ: Implement NSDNAME and NSIP RPZ capabilities X-Git-Tag: rec-4.0.2~1^2~19 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b8470add6a1c801f135582222155a6453b12234e;p=thirdparty%2Fpdns.git RPZ: Implement NSDNAME and NSIP RPZ capabilities Closes #2897 This also adds an extra bool 'wantsRPZ' to the Lua engine so RPZ processing can be disabled for queries (Closes #4226). Furthermore, IPv6 for RPZ is implemented. --- diff --git a/docs/markdown/recursor/scripting.md b/docs/markdown/recursor/scripting.md index 62c1aa9c94..b40d623af4 100644 --- a/docs/markdown/recursor/scripting.md +++ b/docs/markdown/recursor/scripting.md @@ -97,6 +97,7 @@ The DNSQuestion object contains at least the following fields: * policyAction: The action taken by the engine * policyCustom: The CNAME content for the `pdns.policyactions.Custom` response, a string * policyTTL: The TTL in seconds for the `pdns.policyactions.Custom` response +* wantsRPZ - A boolean that indicates the use of the Policy Engine, can be set to `false` in `preresolve` to disable RPZ for this query It also supports the following methods: diff --git a/pdns/filterpo.cc b/pdns/filterpo.cc index 55037f85b2..d24aa83681 100644 --- a/pdns/filterpo.cc +++ b/pdns/filterpo.cc @@ -71,8 +71,19 @@ DNSFilterEngine::Policy DNSFilterEngine::getProcessingPolicy(const DNSName& qnam } } return pol; -} +} +DNSFilterEngine::Policy DNSFilterEngine::getProcessingPolicy(const ComboAddress& address) const +{ + // cout<<"Got question for nameserver IP "<second;; + } + } + return Policy{PolicyKind::NoAction, nullptr, "", 0}; +} DNSFilterEngine::Policy DNSFilterEngine::getQueryPolicy(const DNSName& qname, const ComboAddress& ca) const { @@ -162,6 +173,12 @@ void DNSFilterEngine::addNSTrigger(const DNSName& n, Policy pol, int zone) d_zones[zone].propolName[n]=pol; } +void DNSFilterEngine::addNSIPTrigger(const Netmask& nm, Policy pol, int zone) +{ + assureZones(zone); + d_zones[zone].propolNSAddr.insert(nm).second = pol; +} + bool DNSFilterEngine::rmClientTrigger(const Netmask& nm, Policy pol, int zone) { assureZones(zone); @@ -192,3 +209,11 @@ bool DNSFilterEngine::rmNSTrigger(const DNSName& n, Policy pol, int zone) d_zones[zone].propolName.erase(n); // XXX verify policy matched? =pol; return true; } + +bool DNSFilterEngine::rmNSIPTrigger(const Netmask& nm, Policy pol, int zone) +{ + assureZones(zone); + auto& pols = d_zones[zone].propolNSAddr; + pols.erase(nm); + return true; +} diff --git a/pdns/filterpo.hh b/pdns/filterpo.hh index b153bbc94b..4cabe524cc 100644 --- a/pdns/filterpo.hh +++ b/pdns/filterpo.hh @@ -82,16 +82,19 @@ public: void addClientTrigger(const Netmask& nm, Policy pol, int zone=0); void addQNameTrigger(const DNSName& nm, Policy pol, int zone=0); void addNSTrigger(const DNSName& dn, Policy pol, int zone=0); + void addNSIPTrigger(const Netmask& nm, Policy pol, int zone=0); void addResponseTrigger(const Netmask& nm, Policy pol, int zone=0); bool rmClientTrigger(const Netmask& nm, Policy pol, int zone=0); bool rmQNameTrigger(const DNSName& nm, Policy pol, int zone=0); bool rmNSTrigger(const DNSName& dn, Policy pol, int zone=0); + bool rmNSIPTrigger(const Netmask& nm, Policy pol, int zone=0); bool rmResponseTrigger(const Netmask& nm, Policy pol, int zone=0); Policy getQueryPolicy(const DNSName& qname, const ComboAddress& nm) const; Policy getProcessingPolicy(const DNSName& qname) const; + Policy getProcessingPolicy(const ComboAddress& address) const; Policy getPostPolicy(const vector& records) const; size_t size() { @@ -100,10 +103,11 @@ public: private: void assureZones(int zone); struct Zone { - std::map qpolName; - NetmaskTree qpolAddr; - std::map propolName; - NetmaskTree postpolAddr; + std::map qpolName; // QNAME trigger (RPZ) + NetmaskTree qpolAddr; // Source address + std::map propolName; // NSDNAME (RPZ) + NetmaskTree propolNSAddr; // NSIP (RPZ) + NetmaskTree postpolAddr; // IP trigger (RPZ) }; vector d_zones; diff --git a/pdns/lua-recursor4.cc b/pdns/lua-recursor4.cc index 4018bd2b4d..c3e416f746 100644 --- a/pdns/lua-recursor4.cc +++ b/pdns/lua-recursor4.cc @@ -51,7 +51,7 @@ bool RecursorLua4::postresolve(const ComboAddress& remote,const ComboAddress& lo return false; } -bool RecursorLua4::preresolve(const ComboAddress& remote, const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector& ret, const vector >* ednsOpts, unsigned int tag, DNSFilterEngine::Policy* appliedPolicy, std::vector* policyTags, int& res, bool* variable) +bool RecursorLua4::preresolve(const ComboAddress& remote, const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector& ret, const vector >* ednsOpts, unsigned int tag, DNSFilterEngine::Policy* appliedPolicy, std::vector* policyTags, int& res, bool* variable, bool* wantsRPZ) { return false; } @@ -359,6 +359,7 @@ RecursorLua4::RecursorLua4(const std::string& fname) d_lw->registerMember("udpAnswer", &DNSQuestion::udpAnswer); d_lw->registerMember("udpQueryDest", &DNSQuestion::udpQueryDest); d_lw->registerMember("udpCallback", &DNSQuestion::udpCallback); + d_lw->registerMember("wantsRPZ", &DNSQuestion::wantsRPZ); d_lw->registerMember("appliedPolicy", &DNSQuestion::appliedPolicy); d_lw->registerMember("policyName", &DNSFilterEngine::Policy::d_name); d_lw->registerMember("policyKind", &DNSFilterEngine::Policy::d_kind); @@ -521,29 +522,29 @@ RecursorLua4::RecursorLua4(const std::string& fname) d_gettag = d_lw->readVariable>("gettag").get_value_or(0); } -bool RecursorLua4::preresolve(const ComboAddress& remote,const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector& res, const vector >* ednsOpts, unsigned int tag, DNSFilterEngine::Policy* appliedPolicy, std::vector* policyTags, int& ret, bool* variable) +bool RecursorLua4::preresolve(const ComboAddress& remote,const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector& res, const vector >* ednsOpts, unsigned int tag, DNSFilterEngine::Policy* appliedPolicy, std::vector* policyTags, int& ret, bool* variable, bool* wantsRPZ) { - return genhook(d_preresolve, remote, local, query, qtype, isTcp, res, ednsOpts, tag, appliedPolicy, policyTags, ret, variable); + return genhook(d_preresolve, remote, local, query, qtype, isTcp, res, ednsOpts, tag, appliedPolicy, policyTags, ret, variable, wantsRPZ); } bool RecursorLua4::nxdomain(const ComboAddress& remote,const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector& res, int& ret, bool* variable) { - return genhook(d_nxdomain, remote, local, query, qtype, isTcp, res, 0, 0, nullptr, nullptr, ret, variable); + return genhook(d_nxdomain, remote, local, query, qtype, isTcp, res, 0, 0, nullptr, nullptr, ret, variable, 0); } bool RecursorLua4::nodata(const ComboAddress& remote,const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector& res, int& ret, bool* variable) { - return genhook(d_nodata, remote, local, query, qtype, isTcp, res, 0, 0, nullptr, nullptr, ret, variable); + return genhook(d_nodata, remote, local, query, qtype, isTcp, res, 0, 0, nullptr, nullptr, ret, variable, 0); } bool RecursorLua4::postresolve(const ComboAddress& remote,const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector& res, DNSFilterEngine::Policy* appliedPolicy, std::vector* policyTags, int& ret, bool* variable) { - return genhook(d_postresolve, remote, local, query, qtype, isTcp, res, 0, 0, appliedPolicy, policyTags, ret, variable); + return genhook(d_postresolve, remote, local, query, qtype, isTcp, res, 0, 0, appliedPolicy, policyTags, ret, variable, 0); } bool RecursorLua4::preoutquery(const ComboAddress& ns, const ComboAddress& requestor, const DNSName& query, const QType& qtype, bool isTcp, vector& res, int& ret) { - return genhook(d_preoutquery, ns, requestor, query, qtype, isTcp, res, 0, 0, nullptr, nullptr, ret, 0); + return genhook(d_preoutquery, ns, requestor, query, qtype, isTcp, res, 0, 0, nullptr, nullptr, ret, 0, 0); } bool RecursorLua4::ipfilter(const ComboAddress& remote, const ComboAddress& local, const struct dnsheader& dh) @@ -571,7 +572,7 @@ int RecursorLua4::gettag(const ComboAddress& remote, const Netmask& ednssubnet, return 0; } -bool RecursorLua4::genhook(luacall_t& func, const ComboAddress& remote,const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector& res, const vector >* ednsOpts, unsigned int tag, DNSFilterEngine::Policy* appliedPolicy, std::vector* policyTags, int& ret, bool* variable) +bool RecursorLua4::genhook(luacall_t& func, const ComboAddress& remote,const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector& res, const vector >* ednsOpts, unsigned int tag, DNSFilterEngine::Policy* appliedPolicy, std::vector* policyTags, int& ret, bool* variable, bool* wantsRPZ) { if(!func) return false; @@ -588,8 +589,10 @@ bool RecursorLua4::genhook(luacall_t& func, const ComboAddress& remote,const Com dq->rcode = ret; dq->policyTags = policyTags; dq->appliedPolicy = appliedPolicy; + dq->wantsRPZ = wantsRPZ; bool handled=func(dq); if(variable) *variable |= dq->variable; // could still be set to indicate this *name* is variable, even if not 'handled' + *wantsRPZ = dq->wantsRPZ; // Even if we did not handle the query, RPZ could be disabled if(handled) { loop:; diff --git a/pdns/lua-recursor4.hh b/pdns/lua-recursor4.hh index 3d08a38666..56e51fff9b 100644 --- a/pdns/lua-recursor4.hh +++ b/pdns/lua-recursor4.hh @@ -44,7 +44,7 @@ private: public: explicit RecursorLua4(const std::string& fname); ~RecursorLua4(); // this is so unique_ptr works with an incomplete type - bool preresolve(const ComboAddress& remote,const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector& res, const vector >* ednsOpts, unsigned int tag, DNSFilterEngine::Policy* appliedPolicy, std::vector* policyTags, int& ret, bool* variable); + bool preresolve(const ComboAddress& remote,const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector& res, const vector >* ednsOpts, unsigned int tag, DNSFilterEngine::Policy* appliedPolicy, std::vector* policyTags, int& ret, bool* variable, bool* wantsRPZ); bool nxdomain(const ComboAddress& remote, const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector& res, int& ret, bool* variable); bool nodata(const ComboAddress& remote, const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector& res, int& ret, bool* variable); bool postresolve(const ComboAddress& remote, const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector& res, DNSFilterEngine::Policy* appliedPolicy, std::vector* policyTags, int& ret, bool* variable); @@ -91,11 +91,12 @@ private: DNSFilterEngine::Policy* appliedPolicy; std::vector* policyTags; bool isTcp; + bool wantsRPZ; }; typedef std::function)> luacall_t; luacall_t d_preresolve, d_nxdomain, d_nodata, d_postresolve, d_preoutquery, d_postoutquery; - bool genhook(luacall_t& func, const ComboAddress& remote,const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector& res, const vector >* ednsOpts, unsigned int tag, DNSFilterEngine::Policy* appliedPolicy, std::vector* policyTags, int& ret, bool* variable); + bool genhook(luacall_t& func, const ComboAddress& remote,const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector& res, const vector >* ednsOpts, unsigned int tag, DNSFilterEngine::Policy* appliedPolicy, std::vector* policyTags, int& ret, bool* variable, bool* wantsRPZ); typedef std::function ipfilter_t; ipfilter_t d_ipfilter; }; diff --git a/pdns/pdns_recursor.cc b/pdns/pdns_recursor.cc index 48a1bd9aef..82faf81453 100644 --- a/pdns/pdns_recursor.cc +++ b/pdns/pdns_recursor.cc @@ -660,6 +660,8 @@ void startDoResolve(void *p) auto luaconfsLocal = g_luaconfs.getLocal(); DNSFilterEngine::Policy appliedPolicy; + // Used to tell syncres later on if we should apply NSDNAME and NSIP RPZ triggers for this query + bool wantsRPZ(true); RecProtoBufMessage pbMessage(RecProtoBufMessage::Response); #ifdef HAVE_PROTOBUF if (luaconfsLocal->protobufServer) { @@ -744,47 +746,50 @@ void startDoResolve(void *p) appliedPolicy = dfepol; // if there is a RecursorLua active, and it 'took' the query in preResolve, we don't launch beginResolve - if(!t_pdl->get() || !(*t_pdl)->preresolve(dc->d_remote, dc->d_local, dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), dc->d_tcp, ret, dc->d_ednsOpts.empty() ? 0 : &dc->d_ednsOpts, dc->d_tag, &appliedPolicy, &dc->d_policyTags, res, &variableAnswer)) { - - switch(appliedPolicy.d_kind) { - case DNSFilterEngine::PolicyKind::NoAction: - break; - case DNSFilterEngine::PolicyKind::Drop: - g_stats.policyDrops++; - g_stats.policyResults[appliedPolicy.d_kind]++; - delete dc; - dc=0; - return; - case DNSFilterEngine::PolicyKind::NXDOMAIN: - g_stats.policyResults[appliedPolicy.d_kind]++; - res=RCode::NXDomain; - goto haveAnswer; - case DNSFilterEngine::PolicyKind::NODATA: - g_stats.policyResults[appliedPolicy.d_kind]++; - res=RCode::NoError; - goto haveAnswer; - case DNSFilterEngine::PolicyKind::Custom: - g_stats.policyResults[appliedPolicy.d_kind]++; - res=RCode::NoError; - spoofed.d_name=dc->d_mdp.d_qname; - spoofed.d_type=appliedPolicy.d_custom->getType(); - spoofed.d_ttl = appliedPolicy.d_ttl; - spoofed.d_class = 1; - spoofed.d_content = appliedPolicy.d_custom; - spoofed.d_place = DNSResourceRecord::ANSWER; - ret.push_back(spoofed); - goto haveAnswer; - case DNSFilterEngine::PolicyKind::Truncate: - if(!dc->d_tcp) { + if(!t_pdl->get() || !(*t_pdl)->preresolve(dc->d_remote, dc->d_local, dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), dc->d_tcp, ret, dc->d_ednsOpts.empty() ? 0 : &dc->d_ednsOpts, dc->d_tag, &appliedPolicy, &dc->d_policyTags, res, &variableAnswer, &wantsRPZ)) { + + sr.d_wantsRPZ = wantsRPZ; + if(wantsRPZ) { + switch(appliedPolicy.d_kind) { + case DNSFilterEngine::PolicyKind::NoAction: + break; + case DNSFilterEngine::PolicyKind::Drop: + g_stats.policyDrops++; g_stats.policyResults[appliedPolicy.d_kind]++; - res=RCode::NoError; - pw.getHeader()->tc=1; + delete dc; + dc=0; + return; + case DNSFilterEngine::PolicyKind::NXDOMAIN: + g_stats.policyResults[appliedPolicy.d_kind]++; + res=RCode::NXDomain; goto haveAnswer; - } - break; + case DNSFilterEngine::PolicyKind::NODATA: + g_stats.policyResults[appliedPolicy.d_kind]++; + res=RCode::NoError; + goto haveAnswer; + case DNSFilterEngine::PolicyKind::Custom: + g_stats.policyResults[appliedPolicy.d_kind]++; + res=RCode::NoError; + spoofed.d_name=dc->d_mdp.d_qname; + spoofed.d_type=appliedPolicy.d_custom->getType(); + spoofed.d_ttl = appliedPolicy.d_ttl; + spoofed.d_class = 1; + spoofed.d_content = appliedPolicy.d_custom; + spoofed.d_place = DNSResourceRecord::ANSWER; + ret.push_back(spoofed); + goto haveAnswer; + case DNSFilterEngine::PolicyKind::Truncate: + if(!dc->d_tcp) { + g_stats.policyResults[appliedPolicy.d_kind]++; + res=RCode::NoError; + pw.getHeader()->tc=1; + goto haveAnswer; + } + break; + } } - // Query got not handled for Policy reasons, now actually go out to find an answer + // Query got not handled for QNAME Policy reasons, now actually go out to find an answer try { res = sr.beginResolve(dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), dc->d_mdp.d_qclass, ret); shouldNotValidate = sr.wasOutOfBand(); @@ -795,8 +800,55 @@ void startDoResolve(void *p) res = RCode::ServFail; } - dfepol = luaconfsLocal->dfe.getPostPolicy(ret); - appliedPolicy = dfepol; + // During lookup, an NSDNAME or NSIP trigger was hit in RPZ + if (res == -2) { // XXX This block should be macro'd, it is repeated post-resolve. + appliedPolicy = sr.d_appliedPolicy; + g_stats.policyResults[appliedPolicy.d_kind]++; + switch(appliedPolicy.d_kind) { + case DNSFilterEngine::PolicyKind::NoAction: // This can never happen + throw PDNSException("NoAction policy returned while a NSDNAME or NSIP trigger was hit"); + case DNSFilterEngine::PolicyKind::Drop: + g_stats.policyDrops++; + delete dc; + dc=0; + return; + case DNSFilterEngine::PolicyKind::NXDOMAIN: + ret.clear(); + res=RCode::NXDomain; + goto haveAnswer; + + case DNSFilterEngine::PolicyKind::NODATA: + ret.clear(); + res=RCode::NoError; + goto haveAnswer; + + case DNSFilterEngine::PolicyKind::Truncate: + if(!dc->d_tcp) { + ret.clear(); + res=RCode::NoError; + pw.getHeader()->tc=1; + goto haveAnswer; + } + break; + + case DNSFilterEngine::PolicyKind::Custom: + ret.clear(); + res=RCode::NoError; + spoofed.d_name=dc->d_mdp.d_qname; + spoofed.d_type=appliedPolicy.d_custom->getType(); + spoofed.d_ttl = appliedPolicy.d_ttl; + spoofed.d_class = 1; + spoofed.d_content = appliedPolicy.d_custom; + spoofed.d_place = DNSResourceRecord::ANSWER; + ret.push_back(spoofed); + goto haveAnswer; + } + } + + if (wantsRPZ) { + dfepol = luaconfsLocal->dfe.getPostPolicy(ret); + appliedPolicy = dfepol; + } if(t_pdl->get()) { if(res == RCode::NoError) { @@ -814,45 +866,47 @@ void startDoResolve(void *p) (*t_pdl)->postresolve(dc->d_remote, dc->d_local, dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), dc->d_tcp, ret, &appliedPolicy, &dc->d_policyTags, res, &variableAnswer); } - g_stats.policyResults[appliedPolicy.d_kind]++; - switch(appliedPolicy.d_kind) { - case DNSFilterEngine::PolicyKind::NoAction: - break; - case DNSFilterEngine::PolicyKind::Drop: - g_stats.policyDrops++; - delete dc; - dc=0; - return; - case DNSFilterEngine::PolicyKind::NXDOMAIN: - ret.clear(); - res=RCode::NXDomain; - goto haveAnswer; - - case DNSFilterEngine::PolicyKind::NODATA: - ret.clear(); - res=RCode::NoError; - goto haveAnswer; - - case DNSFilterEngine::PolicyKind::Truncate: - if(!dc->d_tcp) { - ret.clear(); - res=RCode::NoError; - pw.getHeader()->tc=1; - goto haveAnswer; - } - break; - - case DNSFilterEngine::PolicyKind::Custom: - ret.clear(); - res=RCode::NoError; - spoofed.d_name=dc->d_mdp.d_qname; - spoofed.d_type=appliedPolicy.d_custom->getType(); - spoofed.d_ttl = appliedPolicy.d_ttl; - spoofed.d_class = 1; - spoofed.d_content = appliedPolicy.d_custom; - spoofed.d_place = DNSResourceRecord::ANSWER; - ret.push_back(spoofed); - goto haveAnswer; + if (wantsRPZ) { //XXX This block is repeated, see above + g_stats.policyResults[appliedPolicy.d_kind]++; + switch(appliedPolicy.d_kind) { + case DNSFilterEngine::PolicyKind::NoAction: + break; + case DNSFilterEngine::PolicyKind::Drop: + g_stats.policyDrops++; + delete dc; + dc=0; + return; + case DNSFilterEngine::PolicyKind::NXDOMAIN: + ret.clear(); + res=RCode::NXDomain; + goto haveAnswer; + + case DNSFilterEngine::PolicyKind::NODATA: + ret.clear(); + res=RCode::NoError; + goto haveAnswer; + + case DNSFilterEngine::PolicyKind::Truncate: + if(!dc->d_tcp) { + ret.clear(); + res=RCode::NoError; + pw.getHeader()->tc=1; + goto haveAnswer; + } + break; + + case DNSFilterEngine::PolicyKind::Custom: + ret.clear(); + res=RCode::NoError; + spoofed.d_name=dc->d_mdp.d_qname; + spoofed.d_type=appliedPolicy.d_custom->getType(); + spoofed.d_ttl = appliedPolicy.d_ttl; + spoofed.d_class = 1; + spoofed.d_content = appliedPolicy.d_custom; + spoofed.d_place = DNSResourceRecord::ANSWER; + ret.push_back(spoofed); + goto haveAnswer; + } } } haveAnswer:; diff --git a/pdns/rpzloader.cc b/pdns/rpzloader.cc index f326f1504a..c98b136f01 100644 --- a/pdns/rpzloader.cc +++ b/pdns/rpzloader.cc @@ -10,9 +10,52 @@ static Netmask makeNetmaskFromRPZ(const DNSName& name) { auto parts = name.getRawLabels(); - if(parts.size() < 5) + /* + * why 2?, the minimally valid IPv6 address that can be encoded in an RPZ is + * $NETMASK.zz (::/$NETMASK) + * Terrible right? + */ + if(parts.size() < 2 || parts.size() > 9) throw PDNSException("Invalid IP address in RPZ: "+name.toString()); - return Netmask(parts[4]+"."+parts[3]+"."+parts[2]+"."+parts[1]+"/"+parts[0]); + + bool isV6 = (stoi(parts[0]) > 32); + bool hadZZ = false; + + for (auto &part : parts) { + // Check if we have an IPv4 octet + for (auto c : part) + if (!isdigit(c)) + isV6 = true; + + if (pdns_iequals(part,"zz")) { + if (hadZZ) + throw PDNSException("more than one 'zz' label found in RPZ name"+name.toString()); + part = ""; + isV6 = true; + hadZZ = true; + } + } + + if (isV6 && parts.size() < 9 && !hadZZ) + throw PDNSException("No 'zz' label found in an IPv6 RPZ name shorter than 9 elements: "+name.toString()); + + if (parts.size() == 5 && !isV6) + return Netmask(parts[4]+"."+parts[3]+"."+parts[2]+"."+parts[1]+"/"+parts[0]); + + string v6; + + for (uint8_t i = parts.size()-1 ; i > 0; i--) { + v6 += parts[i]; + if (parts[i] == "" && i == 1 && i == parts.size()-1) + v6+= "::"; + if (parts[i] == "" && i != parts.size()-1) + v6+= ":"; + if (parts[i] != "" && i != 1) + v6 += ":"; + } + v6 += "/" + parts[0]; + + return Netmask(v6); } void RPZRecordToPolicy(const DNSRecord& dr, DNSFilterEngine& target, const std::string& polName, bool addOrRemove, boost::optional defpol, int place) @@ -84,9 +127,8 @@ void RPZRecordToPolicy(const DNSRecord& dr, DNSFilterEngine& target, const std:: else target.rmNSTrigger(filt, pol); } else if(dr.d_name.isPartOf(rpzClientIP)) { - - auto nm=makeNetmaskFromRPZ(dr.d_name); - + DNSName filt=dr.d_name.makeRelative(rpzClientIP); + auto nm=makeNetmaskFromRPZ(filt); if(addOrRemove) target.addClientTrigger(nm, pol); else @@ -94,14 +136,19 @@ void RPZRecordToPolicy(const DNSRecord& dr, DNSFilterEngine& target, const std:: } else if(dr.d_name.isPartOf(rpzIP)) { // cerr<<"Should apply answer content IP policy: "<&ret, const vector& records) } } -/** returns -1 in case of no results, rcode otherwise */ +/** returns: + * -1 in case of no results + * -2 when a FilterEngine Policy was hit + * rcode otherwise + */ int SyncRes::doResolveAt(NsSet &nameservers, DNSName auth, bool flawedNSSet, const DNSName &qname, const QType &qtype, vector&ret, int depth, set&beenthere) @@ -952,7 +960,28 @@ int SyncRes::doResolveAt(NsSet &nameservers, DNSName auth, bool flawedNSSet, con prefix.append(depth, ' '); } - LOG(prefix<dfe.getProcessingPolicy(ns.first); + if (d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) { // client query needs an RPZ response + LOG(", however nameserver "<dfe.getProcessingPolicy(address); + if (d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) { // client query needs an RPZ response + LOG(", however nameserver "< rnameservers = shuffleInSpeedOrder(nameservers, doLog() ? (prefix+qname.toString()+": ") : string() ); @@ -993,12 +1022,6 @@ int SyncRes::doResolveAt(NsSet &nameservers, DNSName auth, bool flawedNSSet, con if(!tns->empty()) { LOG(prefix<dfe.getProcessingPolicy(*tns).d_kind != DNSFilterEngine::PolicyKind::NoAction) { - g_stats.policyResults[g_luaconfs.getLocal()->dfe.getProcessingPolicy(*tns).d_kind]++; - throw ImmediateServFailException("Dropped because of policy"); - } if(tns->empty()) { LOG(prefix<toString()); + if (d_wantsRPZ) { + d_appliedPolicy = g_luaconfs.getLocal()->dfe.getProcessingPolicy(*remoteIP); + if (d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) { + hitPolicy = true; + LOG(" (blocked by RPZ policy '"+d_appliedPolicy.d_name+"')"); + } + } } LOG(endl); - + if (hitPolicy) //implies d_wantsRPZ + return -2; } for(remoteIP = remoteIPs.begin(); remoteIP != remoteIPs.end(); ++remoteIP) { @@ -1400,7 +1432,7 @@ int SyncRes::doResolveAt(NsSet &nameservers, DNSName auth, bool flawedNSSet, con return 0; } else if(realreferral) { - LOG(prefix<dnssecmap[newauth]=true; /* for(const auto& e : t_sstorage->dnssecmap) @@ -1410,8 +1442,17 @@ int SyncRes::doResolveAt(NsSet &nameservers, DNSName auth, bool flawedNSSet, con auth=newauth; nameservers.clear(); - for (auto const &nameserver : nsset) + for (auto const &nameserver : nsset) { + if (d_wantsRPZ) { + d_appliedPolicy = g_luaconfs.getLocal()->dfe.getProcessingPolicy(nameserver); + if (d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) { // client query needs an RPZ response + LOG("however "<empty()) { // means: not OOB, OOB == empty diff --git a/pdns/syncres.hh b/pdns/syncres.hh index b86b55a428..95eef6a5c2 100644 --- a/pdns/syncres.hh +++ b/pdns/syncres.hh @@ -368,6 +368,8 @@ public: bool d_wasVariable{false}; bool d_wasOutOfBand{false}; + bool d_wantsRPZ{true}; + DNSFilterEngine::Policy d_appliedPolicy{DNSFilterEngine::PolicyKind::NoAction, nullptr, "", 0}; typedef multi_index_container < NegCacheEntry,