[continued from previous message]
}
STATUS current
DESCRIPTION "Objects conformance group for PowerDNS Recursor"
diff -Nru pdns-recursor-5.2.2/settings/cxxsettings-generated.cc pdns-recursor-5.2.4/settings/cxxsettings-generated.cc
--- pdns-recursor-5.2.2/settings/cxxsettings-generated.cc 2025-04-08 12:42:42.000000000 +0200
+++ pdns-recursor-5.2.4/settings/cxxsettings-generated.cc 2025-07-17 14:22:44.000000000 +0200
@@ -64,6 +64,7 @@
::arg().set("edns-padding-tag", "Packetcache tag associated to responses sent with EDNS padding, to prevent sending these to clients for which padding is not enabled.") = "7830";
::arg().set("edns-subnet-whitelist", "List of netmasks and domains that we should enable EDNS subnet for (deprecated)") = "";
::arg().set("edns-subnet-allow-list", "List of netmasks and domains that we should enable EDNS subnet for") = "";
+ ::arg().setSwitch("edns-subnet-harden", "Do more strict checking or EDNS Client Subnet information returned by authoritative servers") = "no";
::arg().setSwitch("enable-old-settings", "Enable (deprecated) parsing of old-style settings") = "no";
::arg().set("entropy-source", "If set, read entropy from this file") = "/dev/urandom";
::arg().set("etc-hosts-file", "Path to 'hosts' file") = "/etc/hosts";
@@ -310,6 +311,7 @@
settings.outgoing.edns_padding = arg().mustDo("edns-padding-out");
settings.incoming.edns_padding_tag = static_cast<uint64_t>(arg().asNum("edns-padding-tag"));
settings.outgoing.edns_subnet_allow_list = getStrings("edns-subnet-allow-list");
+ settings.outgoing.edns_subnet_harden = arg().mustDo("edns-subnet-harden");
settings.recursor.etc_hosts_file = arg()["etc-hosts-file"];
settings.recursor.event_trace_enabled = static_cast<uint64_t>(arg().asNum("event-trace-enabled"));
settings.recursor.export_etc_hosts = arg().mustDo("export-etc-hosts");
@@ -864,6 +866,13 @@
to_yaml(rustvalue.vec_string_val, value);
return true;
}
+ if (key == "edns-subnet-harden") {
+ section = "outgoing";
+ fieldname = "edns_subnet_harden";
+ type_name = "bool";
+ to_yaml(rustvalue.bool_val, value);
+ return true;
+ }
if (key == "etc-hosts-file") {
section = "recursor";
fieldname = "etc_hosts_file";
@@ -2010,6 +2019,7 @@
::arg().set("edns-padding-out") = to_arg(settings.outgoing.edns_padding);
::arg().set("edns-padding-tag") = to_arg(settings.incoming.edns_padding_tag);
::arg().set("edns-subnet-allow-list") = to_arg(settings.outgoing.edns_subnet_allow_list);
+ ::arg().set("edns-subnet-harden") = to_arg(settings.outgoing.edns_subnet_harden);
::arg().set("etc-hosts-file") = to_arg(settings.recursor.etc_hosts_file);
::arg().set("event-trace-enabled") = to_arg(settings.recursor.event_trace_enabled);
::arg().set("export-etc-hosts") = to_arg(settings.recursor.export_etc_hosts);
diff -Nru pdns-recursor-5.2.2/settings/rust/src/lib.rs pdns-recursor-5.2.4/settings/rust/src/lib.rs
--- pdns-recursor-5.2.2/settings/rust/src/lib.rs 2025-04-08 12:42:42.000000000 +0200
+++ pdns-recursor-5.2.4/settings/rust/src/lib.rs 2025-07-17 14:22:44.000000000 +0200
@@ -925,6 +925,9 @@
edns_subnet_allow_list: Vec<String>,
#[serde(default, skip_serializing_if = "crate::is_default")]
+ edns_subnet_harden: bool,
+
+ #[serde(default, skip_serializing_if = "crate::is_default")]
lowercase: bool,
#[serde(default, skip_serializing_if = "crate::is_default")]
@@ -2048,6 +2051,9 @@
}
merge_vec(&mut self.edns_subnet_allow_list, &mut rhs.edns_subnet_allow_list);
}
+ if m.contains_key("edns_subnet_harden") {
+ rhs.edns_subnet_harden.clone_into(&mut self.edns_subnet_harden);
+ }
if m.contains_key("lowercase") {
rhs.lowercase.clone_into(&mut self.lowercase);
}
diff -Nru pdns-recursor-5.2.2/settings/table.py pdns-recursor-5.2.4/settings/table.py
--- pdns-recursor-5.2.2/settings/table.py 2025-04-08 12:40:39.000000000 +0200
+++ pdns-recursor-5.2.4/settings/table.py 2025-07-17 14:20:08.000000000 +0200
@@ -952,6 +952,18 @@
'versionadded': '4.5.0'
},
{
+ 'name' : 'edns_subnet_harden',
+ 'section' : 'outgoing',
+ 'type' : LType.Bool,
+ 'default' : 'false',
+ 'help' : 'Do more strict checking or EDNS Client Subnet information returned by authoritative servers',
+ 'doc' : '''
+Do more strict checking or EDNS Client Subnet information returned by authoritative servers.
+Answers missing ECS information will be ignored and followed up by an ECS-less query.
+ ''',
+ 'versionadded': ['5.2.x', '5.1.x', '5.0.x']
+ },
+ {
'name' : 'enable_old_settings',
'section' : 'recursor',
'type' : LType.Bool,
diff -Nru pdns-recursor-5.2.2/syncres.cc pdns-recursor-5.2.4/syncres.cc
--- pdns-recursor-5.2.2/syncres.cc 2025-04-08 12:40:39.000000000 +0200
+++ pdns-recursor-5.2.4/syncres.cc 2025-07-17 14:20:08.000000000 +0200
@@ -5490,19 +5490,24 @@
}
}
-bool SyncRes::doResolveAtThisIP(const std::string& prefix, const DNSName& qname, const QType qtype, LWResult& lwr, boost::optional<Netmask>& ednsmask, const DNSName& auth, bool const sendRDQuery, const bool wasForwarded, const DNSName& nsName, const
ComboAddress& remoteIP, bool doTCP, bool doDoT, bool& truncated, bool& spoofed, boost::optional<EDNSExtendedError>& extendedError, bool dontThrottle)
+void SyncRes::checkTotalTime(const DNSName& qname, QType qtype, boost::optional<EDNSExtendedError>& extendedError) const
{
- bool chained = false;
- LWResult::Result resolveret = LWResult::Result::Success;
-
if (s_maxtotusec != 0 && d_totUsec > s_maxtotusec) {
if (s_addExtendedResolutionDNSErrors) {
extendedError = EDNSExtendedError{static_cast<uint16_t>(EDNSExtendedError::code::NoReachableAuthority), "Timeout waiting for answer(s)"};
}
throw ImmediateServFailException("Too much time waiting for " + qname.toLogString() + "|" + qtype.toString() + ", timeouts: " + std::to_string(d_timeouts) + ", throttles: " + std::to_string(d_throttledqueries) + ", queries: " + std::to_string(d_
outqueries) + ", " + std::to_string(d_totUsec / 1000) + " ms");
}
+}
+
+bool SyncRes::doResolveAtThisIP(const std::string& prefix, const DNSName& qname, const QType qtype, LWResult& lwr, boost::optional<Netmask>& ednsmask, const DNSName& auth, bool const sendRDQuery, const bool wasForwarded, const DNSName& nsName, const
ComboAddress& remoteIP, bool doTCP, bool doDoT, bool& truncated, bool& spoofed, boost::optional<EDNSExtendedError>& extendedError, bool dontThrottle)
+{
+ checkTotalTime(qname, qtype, extendedError);
+ bool chained = false;
+ LWResult::Result resolveret = LWResult::Result::Success;
int preOutQueryRet = RCode::NoError;
+
if (d_pdl && d_pdl->preoutquery(remoteIP, d_requestor, qname, qtype, doTCP, lwr.d_records, preOutQueryRet, d_eventTrace, timeval{0, 0})) {
LOG(prefix << qname << ": Query handled by Lua" << endl);
}
@@ -5516,6 +5521,13 @@
resolveret = asyncresolveWrapper(remoteIP, d_doDNSSEC, qname, auth, qtype.getCode(),
doTCP, sendRDQuery, &d_now, ednsmask, &lwr, &chained, nsName); // <- we go out on the wire!
ednsStats(ednsmask, qname, prefix);
+ if (resolveret == LWResult::Result::ECSMissing) {
+ ednsmask = boost::none;
+ LOG(prefix << qname << ": Answer has no ECS, trying again without EDNS Client Subnet Mask" << endl);
+ updateQueryCounts(prefix, qname, remoteIP, doTCP, doDoT);
+ resolveret = asyncresolveWrapper(remoteIP, d_doDNSSEC, qname, auth, qtype.getCode(),
+ doTCP, sendRDQuery, &d_now, ednsmask, &lwr, &chained, nsName);
+ }
}
/* preoutquery killed the query by setting dq.rcode to -3 */
diff -Nru pdns-recursor-5.2.2/syncres.hh pdns-recursor-5.2.4/syncres.hh
--- pdns-recursor-5.2.2/syncres.hh 2025-04-08 12:40:39.000000000 +0200
+++ pdns-recursor-5.2.4/syncres.hh 2025-07-17 14:20:08.000000000 +0200
@@ -634,6 +634,7 @@
std::map<DNSName, std::vector<ComboAddress>>* fallback);
void ednsStats(boost::optional<Netmask>& ednsmask, const DNSName& qname, const string& prefix);
void incTimeoutStats(const ComboAddress& remoteIP);
+ void checkTotalTime(const DNSName& qname, QType qtype, boost::optional<EDNSExtendedError>& extendedError) const;
bool doResolveAtThisIP(const std::string& prefix, const DNSName& qname, QType qtype, LWResult& lwr, boost::optional<Netmask>& ednsmask, const DNSName& auth, bool sendRDQuery, bool wasForwarded, const DNSName& nsName, const ComboAddress& remoteIP,
bool doTCP, bool doDoT, bool& truncated, bool& spoofed, boost::optional<EDNSExtendedError>& extendedError, bool dontThrottle = false);
bool processAnswer(unsigned int depth, const string& prefix, LWResult& lwr, const DNSName& qname, 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, const ComboAddress& remoteIP);
@@ -781,6 +782,7 @@
mutable chain_t authReqChain;
shared_ptr<TCPIOHandler> tcphandler{nullptr};
timeval creationTime{};
+ std::optional<Netmask> ecsSubnet;
string::size_type inPos{0}; // how far are we along in the inMSG
size_t inWanted{0}; // if this is set, we'll read until inWanted bytes are read
string::size_type outPos{0}; // how far we are along in the outMSG
@@ -803,7 +805,7 @@
inline ostream& operator<<(ostream& ostr, const PacketID& pid)
{
- return ostr << "PacketID(id=" << pid.id << ",remote=" << pid.remote.toString() << ",type=" << pid.type << ",tcpsock=" << pid.tcpsock << ",fd=" << pid.fd << ',' << pid.domain << ')';
+ return ostr << "PacketID(id=" << pid.id << ",remote=" << pid.remote.toString() << ",type=" << pid.type << ",tcpsock=" << pid.tcpsock << ",fd=" << pid.fd << ",name=" << pid.domain << ",ecs=" << (pid.ecsSubnet ? pid.ecsSubnet->toString() : "") << ')';
}
inline ostream& operator<<(ostream& ostr, const shared_ptr<PacketID>& pid)
--
Sebastian Ramacher
--- SoupGate-Win32 v1.05
* Origin: fsxNet Usenet Gateway (21:1/5)