return true;
}
+enum class PolicyResult : uint8_t { NoAction, HaveAnswer, Drop };
+
+static PolicyResult handlePolicyHit(const DNSFilterEngine::Policy& appliedPolicy, const std::unique_ptr<DNSComboWriter>& dc, SyncRes& sr, int& res, vector<DNSRecord>& 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 '"<<dc->d_mdp.d_qname<<"' because: "<<e.reason<<endl;
+ }
+ res = RCode::ServFail;
+ break;
+ }
+ catch (const PolicyHitException& e) {
+ if (g_logCommonErrors) {
+ g_log << Logger::Notice << "Sending SERVFAIL to " << dc->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<std::vector<std::unique_ptr<RemoteLogger>>> startProtobufServers(const ProtobufExportConfig& config)
{
return rcode;
}
-enum class PolicyResult : uint8_t { NoAction, HaveAnswer, Drop };
-
-static PolicyResult handlePolicyHit(const DNSFilterEngine::Policy& appliedPolicy, const std::unique_ptr<DNSComboWriter>& dc, SyncRes& sr, int& res, vector<DNSRecord>& 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 '"<<dc->d_mdp.d_qname<<"' because: "<<e.reason<<endl;
- }
- res = RCode::ServFail;
- break;
- }
- catch (const PolicyHitException& e) {
- if (g_logCommonErrors) {
- g_log << Logger::Notice << "Sending SERVFAIL to " << dc->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;
- }
- }
- }
- // Do we have an answer, or only a CNAME match while not looking for CNAME?
- // In the latter case we should call SyncRes.beginResolve() to chase the CNAME.
- bool haveanswer = false;
- for (const auto& r : ret) {
- if (r.d_place == DNSResourceRecord::ANSWER && r.d_type == dc->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<DNSComboWriter>(reinterpret_cast<DNSComboWriter*>(p));
sr.setFrameStreamServers(t_frameStreamServers);
#endif
sr.setQuerySource(dc->d_remote, g_useIncomingECS && !dc->d_ednssubnet.source.empty() ? boost::optional<const EDNSSubnetOpts&>(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;
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) {
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<<Logger::Debug<<"Dropping query because of a filtering policy "<<makeLoginfo(dc)<<endl;
+ return;
+ }
+ catch (const ImmediateServFailException &e) {
if(g_logCommonErrors) {
g_log<<Logger::Notice<<"Sending SERVFAIL to "<<dc->getRemote()<<" during resolve of '"<<dc->d_mdp.d_qname<<"' because: "<<e.reason<<endl;
}
res = RCode::ServFail;
}
- catch(const PolicyHitException& e) {
+ catch (const SendTruncatedAnswerException& e) {
+ ret.clear();
+ res = RCode::NoError;
+ pw.getHeader()->tc = 1;
+ }
+ catch (const PolicyHitException& e) {
res = -2;
}
dq.validationState = sr.getValidationState();
}
}
- 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();
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());
// cout<<dc->d_mdp.d_qname<<"\t"<<MT->getUsec()<<"\t"<<sr.d_outqueries<<endl;
}
- catch(PDNSException &ae) {
+ catch (const PDNSException &ae) {
g_log<<Logger::Error<<"startDoResolve problem "<<makeLoginfo(dc)<<": "<<ae.reason<<endl;
}
- catch(const MOADNSException &mde) {
+ catch (const MOADNSException &mde) {
g_log<<Logger::Error<<"DNS parser error "<<makeLoginfo(dc) <<": "<<dc->d_mdp.d_qname<<", "<<mde.what()<<endl;
}
- catch(std::exception& e) {
+ catch (const std::exception& e) {
g_log<<Logger::Error<<"STL error "<< makeLoginfo(dc)<<": "<<e.what();
// Luawrapper nests the exception from Lua, so we unnest it here
return ret;
}
-bool SyncRes::qnameRPZHit(const DNSFilterEngine& dfe, DNSName& target, const QType& qtype, vector<DNSRecord> &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<DNSRecord>&ret, unsigned int depth, set<GetBestNSAnswer>& 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<CNAMERecordContent>(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<DNSRecord>&ret, unsigned int depth, set<GetBestNSAnswer>& 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);
// moves to three labels per iteration after three iterations.
DNSName child;
- string prefix = d_prefix;
- prefix.append(depth, ' ');
prefix.append(string("QM ") + qname.toString() + "|" + qtype.getName());
QLOG("doResolve");
}
}
- if(doCNAMECacheCheck(qname, qtype, ret, depth, res, state, wasAuthZone, wasForwardRecurse)) { // will reroute us if needed
+ if (doCNAMECacheCheck(qname, qtype, ret, depth, res, state, wasAuthZone, wasForwardRecurse)) { // will reroute us if needed
d_wasOutOfBand = wasAuthZone;
// Do not set *fromCache; res does not reflect the final result in all cases
+ /* Apply Post filtering policies */
+
+ 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(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<<qname<<": No cache hit for '"<<qname<<"|"<<qtype.getName()<<"', trying to find an appropriate NS record"<<endl);
LOG(prefix<<qname<<": initial validation status for "<<qname<<" is "<<state<<endl);
- if(!(res=doResolveAt(nsset, subdomain, flawedNSSet, qname, qtype, ret, depth, beenthere, state, stopAtDelegation)))
+ res = doResolveAt(nsset, subdomain, flawedNSSet, qname, qtype, ret, depth, beenthere, state, stopAtDelegation);
+
+ /* Apply Post filtering policies */
+ 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);
+ }
+ }
+
+ if (!res) {
return 0;
+ }
LOG(prefix<<qname<<": failed (res="<<res<<")"<<endl);
ret.insert(ret.end(), ne.DNSSECRecords.signatures.begin(), ne.DNSSECRecords.signatures.end());
}
+static bool rpzHitShouldReplaceContent(const DNSName& qname, const QType& qtype, const std::vector<DNSRecord>& records)
+{
+ if (qtype == QType::CNAME) {
+ return true;
+ }
+
+ for (const auto& record : records) {
+ if (record.d_type == QType::CNAME) {
+ if (auto content = getRR<CNAMERecordContent>(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<DNSRecord>& 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 "<<qname<<" of type "<<(int)d_appliedPolicy.d_kind<<endl;
+ switch (d_appliedPolicy.d_kind) {
+
+ case DNSFilterEngine::PolicyKind::NoAction:
+ return;
+
+ case DNSFilterEngine::PolicyKind::Drop:
+ ++g_stats.policyDrops;
+ throw ImmediateQueryDropException();
+
+ case DNSFilterEngine::PolicyKind::NXDOMAIN:
+ ret.clear();
+ rcode = RCode::NXDomain;
+ done = true;
+ return;
+
+ case DNSFilterEngine::PolicyKind::NODATA:
+ ret.clear();
+ rcode = RCode::NoError;
+ done = true;
+ return;
+
+ case DNSFilterEngine::PolicyKind::Truncate:
+ if (!d_queryReceivedOverTCP) {
+ ret.clear();
+ rcode = RCode::NoError;
+ throw SendTruncatedAnswerException();
+ }
+ return;
+
+ case DNSFilterEngine::PolicyKind::Custom:
+ {
+ cerr<<"custom"<<endl;
+ if (rpzHitShouldReplaceContent(qname, qtype, ret)) {
+ cerr<<"clearing"<<endl;
+ ret.clear();
+ }
+
+ rcode = RCode::NoError;
+ done = true;
+ auto spoofed = d_appliedPolicy.getCustomRecords(qname, qtype.getCode());
+ for (auto& dr : spoofed) {
+ ret.push_back(dr);
+
+ 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);
+ }
+ }
+ }
+ }
+ }
+}
+
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) {
- 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);
}
const bool oldCacheOnly = setCacheOnly(false);
+ const bool oldWantsRPZ = d_wantsRPZ;
+ d_wantsRPZ = false;
dsmap_t ds;
vState cutState = getDSRecords(end, ds, false, depth);
if (!shouldValidate()) {
setCacheOnly(oldCacheOnly);
+ d_wantsRPZ = oldWantsRPZ;
return;
}
}
}
setCacheOnly(oldCacheOnly);
+ d_wantsRPZ = oldWantsRPZ;
}
vState SyncRes::validateDNSKeys(const DNSName& zone, const std::vector<DNSRecord>& dnskeys, const std::vector<std::shared_ptr<RRSIGRecordContent> >& signatures, unsigned int depth)
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<DNSRecord>& ret, set<DNSName>& 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<DNSRecord>& ret, set<DNSName>& 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;
LOG(prefix<<qname<<": got negative caching indication for name '"<<qname<<"' (accept="<<rec.d_name.isPartOf(auth)<<"), newtarget='"<<newtarget<<"'"<<endl);
rec.d_ttl = min(rec.d_ttl, s_maxnegttl);
- if(newtarget.empty()) // only add a SOA if we're not going anywhere after this
+ // only add a SOA if we're not going anywhere after this
+ if (newtarget.empty()) {
ret.push_back(rec);
+ }
NegCache::NegCacheEntry ne;
}
ret.push_back(rec);
if (auto content = getRR<CNAMERecordContent>(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);
{
LOG(prefix<<qname<<": answer is in: resolved to '"<< rec.d_content->getZoneRepresentation()<<"|"<<DNSRecordContent::NumberToType(rec.d_type)<<"'"<<endl);
- done=true;
+ done = true;
+ rcode = RCode::NoError;
if (state == vState::Secure && needWildcardProof) {
/* We have a positive answer synthesized from a wildcard, we need to check that we have
updateValidationStatusInCache(qname, qtype, lwr.d_aabit, st);
}
}
+
ret.push_back(rec);
}
else if((rec.d_type==QType::RRSIG || rec.d_type==QType::NSEC || rec.d_type==QType::NSEC3) && rec.d_place==DNSResourceRecord::ANSWER) {
}
}
}
- else if(!done && rec.d_place==DNSResourceRecord::AUTHORITY && rec.d_type==QType::SOA &&
- lwr.d_rcode==RCode::NoError && qname.isPartOf(rec.d_name)) {
+ else if (!done && rec.d_place == DNSResourceRecord::AUTHORITY && rec.d_type == QType::SOA &&
+ lwr.d_rcode == RCode::NoError && qname.isPartOf(rec.d_name)) {
LOG(prefix<<qname<<": got negative caching indication for '"<< qname<<"|"<<qtype.getName()<<"'"<<endl);
if(!newtarget.empty()) {
cnamerec.d_type = QType::CNAME;
cnamerec.d_ttl = dnameTTL;
cnamerec.d_content = std::make_shared<CNAMERecordContent>(CNAMERecordContent(newtarget));
- ret.push_back(cnamerec);
+ ret.push_back(std::move(cnamerec));
}
return done;
}
return true;
}
+void SyncRes::handleNewTarget(const std::string& prefix, const DNSName& qname, const DNSName& newtarget, uint16_t qtype, std::vector<DNSRecord>& ret, int& rcode, int depth, const std::vector<DNSRecord>& recordsFromAnswer, vState& state)
+{
+ if (newtarget == qname) {
+ LOG(prefix<<qname<<": status=got a CNAME referral to self, returning SERVFAIL"<<endl);
+ ret.clear();
+ rcode = RCode::ServFail;
+ return;
+ }
+
+ if (depth > 10) {
+ LOG(prefix<<qname<<": status=got a CNAME referral, but recursing too deep, returning SERVFAIL"<<endl);
+ rcode = RCode::ServFail;
+ return;
+ }
+
+ // Check to see if we already have seen the new target as a previous target
+ if (scanForCNAMELoop(newtarget, ret)) {
+ LOG(prefix<<qname<<": status=got a CNAME referral that causes a loop, returning SERVFAIL"<<endl);
+ ret.clear();
+ rcode = RCode::ServFail;
+ return;
+ }
+
+ if (qtype == QType::DS || qtype == QType::DNSKEY) {
+ LOG(prefix<<qname<<": status=got a CNAME referral, but we are looking for a DS or DNSKEY"<<endl);
+
+ if (d_doDNSSEC) {
+ addNXNSECS(ret, recordsFromAnswer);
+ }
+
+ rcode = RCode::NoError;
+ return;
+ }
+
+ LOG(prefix<<qname<<": status=got a CNAME referral, starting over with "<<newtarget<<endl);
+
+ set<GetBestNSAnswer> beenthere;
+ vState cnameState = vState::Indeterminate;
+ rcode = doResolve(newtarget, QType(qtype), ret, depth + 1, beenthere, cnameState);
+ LOG(prefix<<qname<<": updating validation state for response to "<<qname<<" from "<<state<<" with the state from the CNAME quest: "<<cnameState<<endl);
+ updateValidationState(state, cnameState);
+}
+
bool SyncRes::processAnswer(unsigned int depth, LWResult& lwr, const DNSName& qname, const QType& qtype, DNSName& auth, bool wasForwarded, const boost::optional<Netmask> ednsmask, bool sendRDQuery, NsSet &nameservers, std::vector<DNSRecord>& ret, const DNSFilterEngine& dfe, bool* gotNewServers, int* rcode, vState& state)
{
string prefix;
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<<qname<<": status=got results, this level of recursion done"<<endl);
LOG(prefix<<qname<<": validation status is "<<state<<endl);
- *rcode = RCode::NoError;
return true;
}
- if(!newtarget.empty()) {
- if(newtarget == qname) {
- LOG(prefix<<qname<<": status=got a CNAME referral to self, returning SERVFAIL"<<endl);
- ret.clear();
- *rcode = RCode::ServFail;
- return true;
- }
-
- if(depth > 10) {
- LOG(prefix<<qname<<": status=got a CNAME referral, but recursing too deep, returning SERVFAIL"<<endl);
- *rcode = RCode::ServFail;
- return true;
- }
-
- // Check to see if we already have seen the new target as a previous target
- if (scanForCNAMELoop(newtarget, ret)) {
- LOG(prefix<<qname<<": status=got a CNAME referral that causes a loop, returning SERVFAIL"<<endl);
- ret.clear();
- *rcode = RCode::ServFail;
- return true;
- }
-
- if (qtype == QType::DS || qtype == QType::DNSKEY) {
- LOG(prefix<<qname<<": status=got a CNAME referral, but we are looking for a DS or DNSKEY"<<endl);
-
- if(d_doDNSSEC)
- addNXNSECS(ret, lwr.d_records);
-
- *rcode = RCode::NoError;
- return true;
- }
- else {
- LOG(prefix<<qname<<": status=got a CNAME referral, starting over with "<<newtarget<<endl);
-
- set<GetBestNSAnswer> beenthere2;
- vState cnameState = vState::Indeterminate;
- *rcode = doResolve(newtarget, qtype, ret, depth + 1, beenthere2, cnameState);
- LOG(prefix<<qname<<": updating validation state for response to "<<qname<<" from "<<state<<" with the state from the CNAME quest: "<<cnameState<<endl);
- updateValidationState(state, cnameState);
- return true;
- }
+ if (!newtarget.empty()) {
+ handleNewTarget(prefix, qname, newtarget, qtype.getCode(), ret, *rcode, depth, lwr.d_records, state);
+ return true;
}
if(lwr.d_rcode == RCode::NXDomain) {
return d_queryValidationState;
}
+ void setQueryReceivedOverTCP(bool tcp)
+ {
+ d_queryReceivedOverTCP = tcp;
+ }
+
static thread_local ThreadLocalStorage t_sstorage;
static std::atomic<uint64_t> s_queries;
bool doResolveAtThisIP(const std::string& prefix, const DNSName& qname, const QType& qtype, LWResult& lwr, boost::optional<Netmask>& 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<Netmask> ednsmask, bool sendRDQuery, NsSet &nameservers, std::vector<DNSRecord>& ret, const DNSFilterEngine& dfe, bool* gotNewServers, int* rcode, vState& state);
- bool qnameRPZHit(const DNSFilterEngine& dfe, DNSName& target, const QType& qtype, vector<DNSRecord>& ret, unsigned int depth);
int doResolve(const DNSName &qname, const QType &qtype, vector<DNSRecord>&ret, unsigned int depth, set<GetBestNSAnswer>& beenthere, vState& state);
int doResolveNoQNameMinimization(const DNSName &qname, const QType &qtype, vector<DNSRecord>&ret, unsigned int depth, set<GetBestNSAnswer>& beenthere, vState& state, bool* fromCache = NULL, StopAtDelegation* stopAtDelegation = NULL, bool considerforwards = true);
bool doOOBResolve(const AuthDomain& domain, const DNSName &qname, const QType &qtype, vector<DNSRecord>&ret, int& res);
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<Netmask>, 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<DNSRecord>& ret, set<DNSName>& 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<DNSRecord>& ret, set<DNSName>& 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<DNSRecord> &ret);
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<DNSRecord>& ret, int& rcode, int depth, const std::vector<DNSRecord>& recordsFromAnswer, vState& state);
+
+ void handlePolicyHit(const std::string& prefix, const DNSName& qname, const QType& qtype, vector<DNSRecord>& ret, bool& done, int& rcode, unsigned int depth);
+
void setUpdatingRootNS()
{
d_updatingRootNS = true;
bool d_wasOutOfBand{false};
bool d_wasVariable{false};
bool d_qNameMinimization{false};
+ bool d_queryReceivedOverTCP{false};
LogMode d_lm;
};
{
};
+class ImmediateQueryDropException
+{
+};
+
+class SendTruncatedAnswerException
+{
+};
+
typedef boost::circular_buffer<ComboAddress> addrringbuf_t;
extern thread_local std::unique_ptr<addrringbuf_t> t_servfailremotes, t_largeanswerremotes, t_remotes, t_bogusremotes, t_timeouts;