From: Remi Gacogne Date: Tue, 18 Aug 2020 08:45:30 +0000 (+0200) Subject: rec: Handle RPZ CNAME chains, post-policies on cache hits as well X-Git-Tag: rec-4.4.0-beta1~1^2~11 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=d4f08082cfa3ccb59adc1732298bb925d9442d9c;p=thirdparty%2Fpdns.git rec: Handle RPZ CNAME chains, post-policies on cache hits as well --- diff --git a/pdns/filterpo.cc b/pdns/filterpo.cc index 4b329734b9..6a9dc540c9 100644 --- a/pdns/filterpo.cc +++ b/pdns/filterpo.cc @@ -285,38 +285,51 @@ bool DNSFilterEngine::getQueryPolicy(const DNSName& qname, const std::unordered_ } bool DNSFilterEngine::getPostPolicy(const vector& records, const std::unordered_map& discardedPolicies, Policy& pol) const +{ + for (const auto& record : records) { + if (getPostPolicy(record, discardedPolicies, pol)) { + return true; + } + } + + return false; +} + +bool DNSFilterEngine::getPostPolicy(const DNSRecord& record, const std::unordered_map& discardedPolicies, Policy& pol) const { ComboAddress ca; - for (const auto& r : records) { - if (r.d_place != DNSResourceRecord::ANSWER) - continue; - if (r.d_type == QType::A) { - if (auto rec = getRR(r)) { - ca = rec->getCA(); - } + if (record.d_place != DNSResourceRecord::ANSWER) { + return false; + } + + if (record.d_type == QType::A) { + if (auto rec = getRR(record)) { + ca = rec->getCA(); } - else if(r.d_type == QType::AAAA) { - if (auto rec = getRR(r)) { - ca = rec->getCA(); - } + } + else if(record.d_type == QType::AAAA) { + if (auto rec = getRR(record)) { + ca = rec->getCA(); } - else - continue; + } + else { + return false; + } - for (const auto& z : d_zones) { - if (z->getPriority() >= pol.getPriority()) { - break; - } - const auto& zoneName = z->getName(); - if (discardedPolicies.find(zoneName) != discardedPolicies.end()) { - continue; - } + for (const auto& z : d_zones) { + if (z->getPriority() >= pol.getPriority()) { + break; + } + const auto& zoneName = z->getName(); + if (discardedPolicies.find(zoneName) != discardedPolicies.end()) { + return false; + } - if (z->findResponsePolicy(ca, pol)) { - return true; - } + if (z->findResponsePolicy(ca, pol)) { + return true; } } + return false; } diff --git a/pdns/filterpo.hh b/pdns/filterpo.hh index 3828505bf1..021aee75e4 100644 --- a/pdns/filterpo.hh +++ b/pdns/filterpo.hh @@ -347,6 +347,7 @@ public: bool getProcessingPolicy(const DNSName& qname, const std::unordered_map& discardedPolicies, Policy& policy) const; bool getProcessingPolicy(const ComboAddress& address, const std::unordered_map& discardedPolicies, Policy& policy) const; bool getPostPolicy(const vector& records, const std::unordered_map& discardedPolicies, Policy& policy) const; + bool getPostPolicy(const DNSRecord& record, const std::unordered_map& discardedPolicies, Policy& policy) const; // A few convenience methods for the unit test code Policy getQueryPolicy(const DNSName& qname, const std::unordered_map& discardedPolicies, Priority p) const { diff --git a/pdns/pdns_recursor.cc b/pdns/pdns_recursor.cc index 6234460d4e..eb3cd54ecc 100644 --- a/pdns/pdns_recursor.cc +++ b/pdns/pdns_recursor.cc @@ -881,6 +881,75 @@ static bool addRecordToPacket(DNSPacketWriter& pw, const DNSRecord& rec, uint32_ return true; } +enum class PolicyResult : uint8_t { NoAction, HaveAnswer, Drop }; + +static PolicyResult handlePolicyHit(const DNSFilterEngine::Policy& appliedPolicy, const std::unique_ptr& dc, SyncRes& sr, int& res, vector& ret, DNSPacketWriter& pw, bool post) +{ + /* don't account truncate actions for TCP queries, since they are not applied */ + if (appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::Truncate || !dc->d_tcp) { + ++g_stats.policyResults[appliedPolicy.d_kind]; + } + + switch (appliedPolicy.d_kind) { + + case DNSFilterEngine::PolicyKind::NoAction: + return PolicyResult::NoAction; + + case DNSFilterEngine::PolicyKind::Drop: + ++g_stats.policyDrops; + return PolicyResult::Drop; + + case DNSFilterEngine::PolicyKind::NXDOMAIN: + ret.clear(); + res = RCode::NXDomain; + return PolicyResult::HaveAnswer; + + case DNSFilterEngine::PolicyKind::NODATA: + ret.clear(); + res = RCode::NoError; + return PolicyResult::HaveAnswer; + + case DNSFilterEngine::PolicyKind::Truncate: + if (!dc->d_tcp) { + ret.clear(); + res = RCode::NoError; + pw.getHeader()->tc = 1; + return PolicyResult::HaveAnswer; + } + return PolicyResult::NoAction; + + case DNSFilterEngine::PolicyKind::Custom: + res = RCode::NoError; + { + auto spoofed = appliedPolicy.getCustomRecords(dc->d_mdp.d_qname, dc->d_mdp.d_qtype); + for (auto& dr : spoofed) { + ret.push_back(dr); + try { + handleRPZCustom(dr, QType(dc->d_mdp.d_qtype), sr, res, ret); + } + catch (const ImmediateServFailException& e) { + if (g_logCommonErrors) { + g_log << Logger::Notice << "Sending SERVFAIL to " << dc->getRemote() << " during resolve of the custom filter policy '" << appliedPolicy.getName() << "' while resolving '"<d_mdp.d_qname<<"' because: "<getRemote() << " during resolve of the custom filter policy '" << appliedPolicy.getName() << "' while resolving '" << dc->d_mdp.d_qname << "' because another RPZ policy was hit" << endl; + } + res = RCode::ServFail; + break; + } + } + + return PolicyResult::HaveAnswer; + } + } + + return PolicyResult::NoAction; +} + #ifdef HAVE_PROTOBUF static std::shared_ptr>> startProtobufServers(const ProtobufExportConfig& config) { @@ -1194,104 +1263,6 @@ int getFakePTRRecords(const DNSName& qname, vector& ret) return rcode; } -enum class PolicyResult : uint8_t { NoAction, HaveAnswer, Drop }; - -static PolicyResult handlePolicyHit(const DNSFilterEngine::Policy& appliedPolicy, const std::unique_ptr& dc, SyncRes& sr, int& res, vector& ret, DNSPacketWriter& pw, bool post) -{ - /* don't account truncate actions for TCP queries, since they are not applied */ - if (appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::Truncate || !dc->d_tcp) { - ++g_stats.policyResults[appliedPolicy.d_kind]; - } - - switch (appliedPolicy.d_kind) { - - case DNSFilterEngine::PolicyKind::NoAction: - return PolicyResult::NoAction; - - case DNSFilterEngine::PolicyKind::Drop: - ++g_stats.policyDrops; - return PolicyResult::Drop; - - case DNSFilterEngine::PolicyKind::NXDOMAIN: - ret.clear(); - res = RCode::NXDomain; - return PolicyResult::HaveAnswer; - - case DNSFilterEngine::PolicyKind::NODATA: - ret.clear(); - res = RCode::NoError; - return PolicyResult::HaveAnswer; - - case DNSFilterEngine::PolicyKind::Truncate: - if (!dc->d_tcp) { - ret.clear(); - res = RCode::NoError; - pw.getHeader()->tc = 1; - return PolicyResult::HaveAnswer; - } - return PolicyResult::NoAction; - - case DNSFilterEngine::PolicyKind::Custom: - res = RCode::NoError; - //cerr << "current answer(" << post << ") Q: " << dc->d_mdp.d_qname << '/' << QType(dc->d_mdp.d_qtype).getName() << endl; - //for (auto r : ret) { - // cerr << r.d_place << ' ' << r.d_name << ' ' << QType(r.d_type).getName() << ' ' << r.d_content->getZoneRepresentation() << endl; - //} - //cerr << "------------" << endl; - // In some cases, the policy should extend the result vector and in some cases replace - // We extend if the current vector contains a CNAME we found while resolving a non-CNAME - // This is all very ugly, but ATM I don't know a better approach... - if (dc->d_mdp.d_qtype != QType::CNAME) { - bool cname = false; - for (const auto& r : ret) { - if (r.d_place == DNSResourceRecord::ANSWER && r.d_type == QType::CNAME) { - cname = true; - break; - } - } - if (!cname) { - ret.clear(); - } - } - if (post && ret.size() == 0) { // can happen with NS matches, those do not fill the result, fallback to original behaviour - auto spoofed = appliedPolicy.getCustomRecords(dc->d_mdp.d_qname, dc->d_mdp.d_qtype); - for (auto& dr : spoofed) { - ret.push_back(dr); - try { - handleRPZCustom(dr, QType(dc->d_mdp.d_qtype), sr, res, ret); - } - catch (const ImmediateServFailException& e) { - if (g_logCommonErrors) { - g_log << Logger::Notice << "Sending SERVFAIL to " << dc->getRemote() << " during resolve of the custom filter policy '" << appliedPolicy.getName() << "' while resolving '"<d_mdp.d_qname<<"' because: "<getRemote() << " during resolve of the custom filter policy '" << appliedPolicy.getName() << "' while resolving '"<d_mdp.d_qname<<"' because another RPZ policy was hit"<d_mdp.d_qtype) { - haveanswer = true; - break; - } - } - - return haveanswer ? PolicyResult::HaveAnswer : PolicyResult::NoAction; - } - - return PolicyResult::NoAction; -} - static void startDoResolve(void *p) { auto dc=std::unique_ptr(reinterpret_cast(p)); @@ -1423,6 +1394,7 @@ static void startDoResolve(void *p) sr.setFrameStreamServers(t_frameStreamServers); #endif sr.setQuerySource(dc->d_remote, g_useIncomingECS && !dc->d_ednssubnet.source.empty() ? boost::optional(dc->d_ednssubnet) : boost::none); + sr.setQueryReceivedOverTCP(dc->d_tcp); bool tracedQuery=false; // we could consider letting Lua know about this too bool shouldNotValidate = false; @@ -1487,22 +1459,34 @@ static void startDoResolve(void *p) if (luaconfsLocal->dfe.getClientPolicy(dc->d_source, sr.d_discardedPolicies, appliedPolicy)) { mergePolicyTags(dc->d_policyTags, appliedPolicy.getTags()); } - - if ((appliedPolicy.d_type == DNSFilterEngine::PolicyType::None || appliedPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction)) { - if (luaconfsLocal->dfe.getQueryPolicy(dc->d_mdp.d_qname, 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 */ + if (dc->d_rcode != boost::none) { + + bool policyOverride = false; + /* Unless we already matched on the client IP, time to check the qname. + We normally check it in beginResolve() but it will be bypassed since we already have an answer */ + if (wantsRPZ && appliedPolicy.policyOverridesGettag()) { + if (appliedPolicy.d_type != DNSFilterEngine::PolicyType::None) { + // Client IP already matched + } + else { + // no match on the client IP, check the qname + if (luaconfsLocal->dfe.getQueryPolicy(dc->d_mdp.d_qname, sr.d_discardedPolicies, appliedPolicy)) { + // got a match + mergePolicyTags(dc->d_policyTags, appliedPolicy.getTags()); + } + } + + if (appliedPolicy.d_type != DNSFilterEngine::PolicyType::None && appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) { + policyOverride = true; } } - } - // If we are doing RPZ and a policy was matched on the qname, it normally takes precedence over an answer from gettag. - // So process the gettag_ffi answer only if no RPZ action was matched or the policy indicates gettag should - // have precedence. - // Note that this case will only be entered if the policy hit was on the client or on the original qname, - // hits while chasing CNAMEs will not be known here yet, since they only will be discovered by SyncRes.beginResolve(). - if (!wantsRPZ || !appliedPolicy.policyOverridesGettag() || appliedPolicy.d_type == DNSFilterEngine::PolicyType::None) { - if (dc->d_rcode != boost::none) { - /* we have a response ready to go, most likely from gettag_ffi */ + if (policyOverride) { + /* No RPZ or gettag overrides it anyway */ ret = std::move(dc->d_records); res = *dc->d_rcode; if (res == RCode::NoError && dc->d_followCNAMERecords) { @@ -1544,13 +1528,24 @@ static void startDoResolve(void *p) res = sr.beginResolve(dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), dc->d_mdp.d_qclass, ret); shouldNotValidate = sr.wasOutOfBand(); } - catch(const ImmediateServFailException &e) { + catch (const ImmediateQueryDropException& e) { +#warning We need to export a protobuf (and NOD lookup?) message if requested! + g_stats.policyDrops++; + g_log<getRemote()<<" during resolve of '"<d_mdp.d_qname<<"' because: "<tc = 1; + } + catch (const PolicyHitException& e) { res = -2; } dq.validationState = sr.getValidationState(); @@ -1571,12 +1566,6 @@ static void startDoResolve(void *p) } } - if (wantsRPZ && (appliedPolicy.d_type == DNSFilterEngine::PolicyType::None || appliedPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction)) { - if (luaconfsLocal->dfe.getPostPolicy(ret, sr.d_discardedPolicies, appliedPolicy)) { - mergePolicyTags(dc->d_policyTags, appliedPolicy.getTags()); - } - } - if (t_pdl || (g_dns64Prefix && dq.qtype == QType::AAAA && dq.validationState != vState::Bogus)) { if (res == RCode::NoError) { auto i = ret.cbegin(); @@ -1606,23 +1595,9 @@ static void startDoResolve(void *p) shouldNotValidate = true; } } - - if (wantsRPZ) { //XXX This block is repeated, see above - - auto policyResult = handlePolicyHit(appliedPolicy, dc, sr, res, ret, pw, true); - if (policyResult == PolicyResult::HaveAnswer) { - goto haveAnswer; - } - else if (policyResult == PolicyResult::Drop) { - return; - } - } } + haveAnswer:; - if(res == PolicyDecision::DROP) { - g_stats.policyDrops++; - return; - } if(tracedQuery || res == -1 || res == RCode::ServFail || pw.getHeader()->rcode == RCode::ServFail) { string trace(sr.getTrace()); @@ -2002,13 +1977,13 @@ static void startDoResolve(void *p) // cout<d_mdp.d_qname<<"\t"<getUsec()<<"\t"<d_mdp.d_qname<<", "< &ret, unsigned int depth) -{ - if (!d_wantsRPZ) { - return false; - } - if (s_maxdepth && depth > s_maxdepth) { - string prefix = d_prefix; - prefix.append(depth, ' '); - string msg = "More than " + std::to_string(s_maxdepth) + " (max-recursion-depth) levels of recursion needed while resolving " + target.toLogString(); - LOG(prefix << target << ": " << msg << endl); - throw ImmediateServFailException(msg); - } +#define QLOG(x) LOG(prefix << " child=" << child << ": " << x << endl) - bool match = dfe.getQueryPolicy(target, d_discardedPolicies, d_appliedPolicy, true); - if (!match) { - return false; - } +int SyncRes::doResolve(const DNSName &qname, const QType &qtype, vector&ret, unsigned int depth, set& beenthere, vState& state) { - mergePolicyTags(d_policyTags, d_appliedPolicy.getTags()); - if (d_appliedPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction) { - return false; - } - LOG(": (hit by RPZ policy '" + d_appliedPolicy.getName() + "')" << endl); - if (d_appliedPolicy.d_kind == DNSFilterEngine::PolicyKind::Truncate) { - // XXX We don't know if we're doing TCP here.... - return false; - } - if (d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::Custom) { - return true; - } - auto spoofed = d_appliedPolicy.getCustomRecords(target, qtype.getCode()); + string prefix = d_prefix; + prefix.append(depth, ' '); + auto luaconfsLocal = g_luaconfs.getLocal(); - // Add the record to the result vector being built, chase if we hit a CNAME - for (const auto& dr : spoofed) { - if (dr.d_place != DNSResourceRecord::ANSWER) { - continue; - } - ret.push_back(dr); - switch (dr.d_type) { - case QType::CNAME: - auto cnamecontent = getRR(dr); - if (cnamecontent) { - target = cnamecontent->getTarget(); - // This call wil return true if we hit a policy that needs an throw PolicyHitException - // For CNAME chasing, we don't want that since resolving should continue with the new target - return qnameRPZHit(dfe, target, qtype, ret, depth + 1); + /* Apply qname (including CNAME chain) filtering policies */ + if (d_wantsRPZ && (d_appliedPolicy.d_type == DNSFilterEngine::PolicyType::None || d_appliedPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction)) { + if (luaconfsLocal->dfe.getQueryPolicy(qname, d_discardedPolicies, d_appliedPolicy)) { + mergePolicyTags(d_policyTags, d_appliedPolicy.getTags()); + bool done = false; + int rcode = RCode::NoError; + handlePolicyHit(prefix, qname, qtype, ret, done, rcode, depth); + if (done) { + return rcode; } - break; } } - return true; -} - -#define QLOG(x) LOG(prefix << " child=" << child << ": " << x << endl) - -int SyncRes::doResolve(const DNSName &qnameArg, const QType &qtype, vector&ret, unsigned int depth, set& beenthere, vState& state) { - - DNSName qname(qnameArg); - auto luaconfsLocal = g_luaconfs.getLocal(); - - // Can change qname - bool hit = qnameRPZHit(luaconfsLocal->dfe, qname, qtype, ret, depth + 1); - if (hit) { - throw PolicyHitException(); - } // In the auth or recursive forward case, it does not make sense to do qname-minimization if (!getQNameMinimization() || isRecursiveForwardOrAuth(qname)) { return doResolveNoQNameMinimization(qname, qtype, ret, depth, beenthere, state); @@ -725,8 +680,6 @@ int SyncRes::doResolve(const DNSName &qnameArg, const QType &qtype, vectordfe.getPostPolicy(ret, d_discardedPolicies, d_appliedPolicy)) { + mergePolicyTags(d_policyTags, d_appliedPolicy.getTags()); + bool done = false; + handlePolicyHit(prefix, qname, qtype, ret, done, res, depth); + } + } + return res; } - if(doCacheCheck(qname, authname, wasForwardedOrAuthZone, wasAuthZone, wasForwardRecurse, qtype, ret, depth, res, state)) { + if (doCacheCheck(qname, authname, wasForwardedOrAuthZone, wasAuthZone, wasForwardRecurse, qtype, ret, depth, res, state)) { // we done d_wasOutOfBand = wasAuthZone; - if (fromCache) + if (fromCache) { *fromCache = true; + } + + if (d_wantsRPZ && (d_appliedPolicy.d_type == DNSFilterEngine::PolicyType::None || d_appliedPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction)) { + auto luaLocal = g_luaconfs.getLocal(); + if (luaLocal->dfe.getPostPolicy(ret, d_discardedPolicies, d_appliedPolicy)) { + mergePolicyTags(d_policyTags, d_appliedPolicy.getTags()); + bool done = false; + handlePolicyHit(prefix, qname, qtype, ret, done, res, depth); + } + } + return res; } } - if(d_cacheonly) + if (d_cacheonly) { return 0; + } LOG(prefix<dfe.getPostPolicy(ret, d_discardedPolicies, d_appliedPolicy)) { + mergePolicyTags(d_policyTags, d_appliedPolicy.getTags()); + bool done = false; + handlePolicyHit(prefix, qname, qtype, ret, done, res, depth); + } + } + + if (!res) { return 0; + } LOG(prefix<&ret, const vector& records) ret.insert(ret.end(), ne.DNSSECRecords.signatures.begin(), ne.DNSSECRecords.signatures.end()); } +static bool rpzHitShouldReplaceContent(const DNSName& qname, const QType& qtype, const std::vector& records) +{ + if (qtype == QType::CNAME) { + return true; + } + + for (const auto& record : records) { + if (record.d_type == QType::CNAME) { + if (auto content = getRR(record)) { + if (qname == content->getTarget()) { + /* we have a CNAME whose target matches the entry we are about to + generate, so it will complete the current records, not replace + them + */ + return false; + } + } + } + } + + return true; +} + +void SyncRes::handlePolicyHit(const std::string& prefix, const DNSName& qname, const QType& qtype, std::vector& ret, bool& done, int& rcode, unsigned int depth) +{ + /* 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 "<(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); + } + } + } + } + } +} + bool SyncRes::nameserversBlockedByRPZ(const DNSFilterEngine& dfe, const NsSet& nameservers) { /* we skip RPZ processing if: - it was disabled (d_wantsRPZ is false) ; - we already got a RPZ hit (d_appliedPolicy.d_type != DNSFilterEngine::PolicyType::None) since the only way we can get back here is that it was a 'pass-thru' (NoAction) meaning that we should not - process any further RPZ rules. + process any further RPZ rules. Except that we need to process rules of higher priority.. */ if (d_wantsRPZ && (d_appliedPolicy.d_type == DNSFilterEngine::PolicyType::None || d_appliedPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction)) { for (auto const &ns : nameservers) { @@ -1992,7 +2068,7 @@ bool SyncRes::nameserverIPBlockedByRPZ(const DNSFilterEngine& dfe, const ComboAd - it was disabled (d_wantsRPZ is false) ; - we already got a RPZ hit (d_appliedPolicy.d_type != DNSFilterEngine::PolicyType::None) since the only way we can get back here is that it was a 'pass-thru' (NoAction) meaning that we should not - process any further RPZ rules. + process any further RPZ rules. Except that we need to process rules of higher priority.. */ if (d_wantsRPZ && (d_appliedPolicy.d_type == DNSFilterEngine::PolicyType::None || d_appliedPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction)) { bool match = dfe.getProcessingPolicy(remoteIP, d_discardedPolicies, d_appliedPolicy); @@ -2321,6 +2397,8 @@ void SyncRes::computeZoneCuts(const DNSName& begin, const DNSName& end, unsigned } const bool oldCacheOnly = setCacheOnly(false); + const bool oldWantsRPZ = d_wantsRPZ; + d_wantsRPZ = false; dsmap_t ds; vState cutState = getDSRecords(end, ds, false, depth); @@ -2329,6 +2407,7 @@ void SyncRes::computeZoneCuts(const DNSName& begin, const DNSName& end, unsigned if (!shouldValidate()) { setCacheOnly(oldCacheOnly); + d_wantsRPZ = oldWantsRPZ; return; } @@ -2397,6 +2476,7 @@ void SyncRes::computeZoneCuts(const DNSName& begin, const DNSName& end, unsigned } } setCacheOnly(oldCacheOnly); + d_wantsRPZ = oldWantsRPZ; } vState SyncRes::validateDNSKeys(const DNSName& zone, const std::vector& dnskeys, const std::vector >& signatures, unsigned int depth) @@ -3050,7 +3130,7 @@ dState SyncRes::getDenialValidationState(const NegCache::NegCacheEntry& ne, cons return getDenial(csp, ne.d_name, ne.d_qtype.getCode(), referralToUnsigned, expectedState == dState::NXQTYPE); } -bool SyncRes::processRecords(const std::string& prefix, const DNSName& qname, const QType& qtype, const DNSName& auth, LWResult& lwr, const bool sendRDQuery, vector& ret, set& nsset, DNSName& newtarget, DNSName& newauth, bool& realreferral, bool& negindic, vState& state, const bool needWildcardProof, const bool gatherWildcardProof, const unsigned int wildcardLabelsCount) +bool SyncRes::processRecords(const std::string& prefix, const DNSName& qname, const QType& qtype, const DNSName& auth, LWResult& lwr, const bool sendRDQuery, vector& ret, set& nsset, DNSName& newtarget, DNSName& newauth, bool& realreferral, bool& negindic, vState& state, const bool needWildcardProof, const bool gatherWildcardProof, const unsigned int wildcardLabelsCount, int& rcode, unsigned int depth) { bool done = false; DNSName dnameTarget, dnameOwner; @@ -3073,8 +3153,10 @@ bool SyncRes::processRecords(const std::string& prefix, const DNSName& qname, co LOG(prefix<(rec)) { - newtarget=content->getTarget(); + newtarget=DNSName(content->getTarget()); } } else if (rec.d_type == QType::DNAME && qname.isPartOf(rec.d_name)) { // DNAME ret.push_back(rec); @@ -3167,7 +3249,8 @@ bool SyncRes::processRecords(const std::string& prefix, const DNSName& qname, co { LOG(prefix<getZoneRepresentation()<<"|"<(CNAMERecordContent(newtarget)); - ret.push_back(cnamerec); + ret.push_back(std::move(cnamerec)); } return done; } @@ -3464,6 +3548,49 @@ bool SyncRes::doResolveAtThisIP(const std::string& prefix, const DNSName& qname, return true; } +void SyncRes::handleNewTarget(const std::string& prefix, const DNSName& qname, const DNSName& newtarget, uint16_t qtype, std::vector& ret, int& rcode, int depth, const std::vector& recordsFromAnswer, vState& state) +{ + if (newtarget == qname) { + LOG(prefix< 10) { + LOG(prefix< beenthere; + vState cnameState = vState::Indeterminate; + rcode = doResolve(newtarget, QType(qtype), ret, depth + 1, beenthere, cnameState); + LOG(prefix< ednsmask, bool sendRDQuery, NsSet &nameservers, std::vector& ret, const DNSFilterEngine& dfe, bool* gotNewServers, int* rcode, vState& state) { string prefix; @@ -3503,56 +3630,17 @@ bool SyncRes::processAnswer(unsigned int depth, LWResult& lwr, const DNSName& qn DNSName newauth; DNSName newtarget; - bool done = processRecords(prefix, qname, qtype, auth, lwr, sendRDQuery, ret, nsset, newtarget, newauth, realreferral, negindic, state, needWildcardProof, gatherWildcardProof, wildcardLabelsCount); + bool done = processRecords(prefix, qname, qtype, auth, lwr, sendRDQuery, ret, nsset, newtarget, newauth, realreferral, negindic, state, needWildcardProof, gatherWildcardProof, wildcardLabelsCount, *rcode, depth); - if(done){ + if (done){ LOG(prefix< 10) { - LOG(prefix< beenthere2; - vState cnameState = vState::Indeterminate; - *rcode = doResolve(newtarget, qtype, ret, depth + 1, beenthere2, cnameState); - LOG(prefix< s_queries; @@ -823,7 +828,6 @@ private: bool doResolveAtThisIP(const std::string& prefix, const DNSName& qname, const QType& qtype, LWResult& lwr, boost::optional& ednsmask, const DNSName& auth, bool const sendRDQuery, const DNSName& nsName, const ComboAddress& remoteIP, bool doTCP, bool* truncated); bool processAnswer(unsigned int depth, LWResult& lwr, const DNSName& qname, const QType& qtype, DNSName& auth, bool wasForwarded, const boost::optional ednsmask, bool sendRDQuery, NsSet &nameservers, std::vector& ret, const DNSFilterEngine& dfe, bool* gotNewServers, int* rcode, vState& state); - bool qnameRPZHit(const DNSFilterEngine& dfe, DNSName& target, const QType& qtype, vector& ret, unsigned int depth); int doResolve(const DNSName &qname, const QType &qtype, vector&ret, unsigned int depth, set& beenthere, vState& state); int doResolveNoQNameMinimization(const DNSName &qname, const QType &qtype, vector&ret, unsigned int depth, set& beenthere, vState& state, bool* fromCache = NULL, StopAtDelegation* stopAtDelegation = NULL, bool considerforwards = true); bool doOOBResolve(const AuthDomain& domain, const DNSName &qname, const QType &qtype, vector&ret, int& res); @@ -848,7 +852,7 @@ private: void sanitizeRecords(const std::string& prefix, LWResult& lwr, const DNSName& qname, const QType& qtype, const DNSName& auth, bool wasForwarded, bool rdQuery); RCode::rcodes_ updateCacheFromRecords(unsigned int depth, LWResult& lwr, const DNSName& qname, const QType& qtype, const DNSName& auth, bool wasForwarded, const boost::optional, vState& state, bool& needWildcardProof, bool& gatherWildcardProof, unsigned int& wildcardLabelsCount, bool sendRDQuery); - bool processRecords(const std::string& prefix, const DNSName& qname, const QType& qtype, const DNSName& auth, LWResult& lwr, const bool sendRDQuery, vector& ret, set& nsset, DNSName& newtarget, DNSName& newauth, bool& realreferral, bool& negindic, vState& state, const bool needWildcardProof, const bool gatherwildcardProof, const unsigned int wildcardLabelsCount); + bool processRecords(const std::string& prefix, const DNSName& qname, const QType& qtype, const DNSName& auth, LWResult& lwr, const bool sendRDQuery, vector& ret, set& nsset, DNSName& newtarget, DNSName& newauth, bool& realreferral, bool& negindic, vState& state, const bool needWildcardProof, const bool gatherwildcardProof, const unsigned int wildcardLabelsCount, int& rcode, unsigned int depth); bool doSpecialNamesResolve(const DNSName &qname, const QType &qtype, const uint16_t qclass, vector &ret); @@ -873,6 +877,10 @@ private: bool lookForCut(const DNSName& qname, unsigned int depth, const vState existingState, vState& newState); void computeZoneCuts(const DNSName& begin, const DNSName& end, unsigned int depth); + void handleNewTarget(const std::string& prefix, const DNSName& qname, const DNSName& newtarget, uint16_t qtype, std::vector& ret, int& rcode, int depth, const std::vector& recordsFromAnswer, vState& state); + + void handlePolicyHit(const std::string& prefix, const DNSName& qname, const QType& qtype, vector& ret, bool& done, int& rcode, unsigned int depth); + void setUpdatingRootNS() { d_updatingRootNS = true; @@ -905,6 +913,7 @@ private: bool d_wasOutOfBand{false}; bool d_wasVariable{false}; bool d_qNameMinimization{false}; + bool d_queryReceivedOverTCP{false}; LogMode d_lm; }; @@ -1067,6 +1076,14 @@ class PolicyHitException { }; +class ImmediateQueryDropException +{ +}; + +class SendTruncatedAnswerException +{ +}; + typedef boost::circular_buffer addrringbuf_t; extern thread_local std::unique_ptr t_servfailremotes, t_largeanswerremotes, t_remotes, t_bogusremotes, t_timeouts;