struct EDNSOptionCode
{
- enum EDNSOptionCodeEnum {NSID=3, DAU=5, DHU=6, N3U=7, ECS=8, EXPIRE=9, COOKIE=10, TCPKEEPALIVE=11, PADDING=12, CHAIN=13, KEYTAG=14};
+ enum EDNSOptionCodeEnum {NSID=3, DAU=5, DHU=6, N3U=7, ECS=8, EXPIRE=9, COOKIE=10, TCPKEEPALIVE=11, PADDING=12, CHAIN=13, KEYTAG=14, EXTENDEDERROR=15};
};
/* extract a specific EDNS0 option from a pointer on the beginning rdLen of the OPT RR */
#include "validate-recursor.hh"
#include "rec-lua-conf.hh"
#include "ednsoptions.hh"
+#include "ednsextendederror.hh"
#include "gettime.hh"
#include "proxy-protocol.hh"
#include "pubsuffix.hh"
static uint16_t s_minUdpSourcePort;
static uint16_t s_maxUdpSourcePort;
static double s_balancingFactor;
+static bool s_addExtendedDNSErrors;
RecursorControlChannel s_rcc; // only active in the handler thread
RecursorStats g_stats;
sa.reset();
sa.sin4.sin_family = eo.source.getNetwork().sin4.sin_family;
eo.scope = Netmask(sa, 0);
+ auto ecsPayload = makeEDNSSubnetOptsString(eo);
- returnedEdnsOptions.push_back(make_pair(EDNSOptionCode::ECS, makeEDNSSubnetOptsString(eo)));
+ maxanswersize -= 2 + 2 + ecsPayload.size();
+
+ returnedEdnsOptions.push_back(make_pair(EDNSOptionCode::ECS, std::move(ecsPayload)));
}
if (haveEDNS) {
+ auto state = sr.getValidationState();
+ if (s_addExtendedDNSErrors && vStateIsBogus(state) && pw.size() < maxanswersize && (maxanswersize - pw.size()) > (2 + 2 + 2)) {
+ EDNSExtendedError::code code;
+
+ switch (state) {
+ case vState::BogusNoValidDNSKEY:
+ code = EDNSExtendedError::code::DNSKEYMissing;
+ break;
+ case vState::BogusInvalidDenial:
+ code = EDNSExtendedError::code::NSECMissing;
+ break;
+ case vState::BogusUnableToGetDSs:
+ code = EDNSExtendedError::code::DNSSECBogus;
+ break;
+ case vState::BogusUnableToGetDNSKEYs:
+ code = EDNSExtendedError::code::DNSKEYMissing;
+ break;
+ case vState::BogusSelfSignedDS:
+ code = EDNSExtendedError::code::DNSSECBogus;
+ break;
+ case vState::BogusNoRRSIG:
+ code = EDNSExtendedError::code::RRSIGsMissing;
+ break;
+ case vState::BogusNoValidRRSIG:
+ code = EDNSExtendedError::code::DNSSECBogus;
+ break;
+ case vState::BogusMissingNegativeIndication:
+ code = EDNSExtendedError::code::NSECMissing;
+ break;
+ case vState::BogusSignatureNotYetValid:
+ code = EDNSExtendedError::code::SignatureNotYetValid;
+ break;
+ case vState::BogusSignatureExpired:
+ code = EDNSExtendedError::code::SignatureExpired;
+ break;
+ case vState::BogusUnsupportedDNSKEYAlgo:
+ code = EDNSExtendedError::code::UnsupportedDNSKEYAlgorithm;
+ break;
+ case vState::BogusUnsupportedDSDigestType:
+ code = EDNSExtendedError::code::UnsupportedDSDigestType;
+ break;
+ case vState::BogusNoZoneKeyBitSet:
+ code = EDNSExtendedError::code::NoZoneKeyBitSet;
+ break;
+ case vState::BogusRevokedDNSKEY:
+ code = EDNSExtendedError::code::DNSSECBogus;
+ break;
+ case vState::BogusInvalidDNSKEYProtocol:
+ code = EDNSExtendedError::code::DNSSECBogus;
+ break;
+ default:
+ throw std::runtime_error("Bogus validation state not handled: " + vStateToString(state));
+ }
+
+ EDNSExtendedError eee;
+ eee.infoCode = static_cast<uint16_t>(code);
+ returnedEdnsOptions.push_back(make_pair(EDNSOptionCode::EXTENDEDERROR, makeEDNSExtendedErrorOptString(eee)));
+ }
+
/* we try to add the EDNS OPT RR even for truncated answers,
as rfc6891 states:
"The minimal response MUST be the DNS header, question section, and an
} else {
TCPConnection::s_maxInFlight = maxInFlight;
}
-
g_gettagNeedsEDNSOptions = ::arg().mustDo("gettag-needs-edns-options");
g_statisticsInterval = ::arg().asNum("statistics-interval");
+ s_addExtendedDNSErrors = ::arg().mustDo("extended-errors");
+
{
SuffixMatchNode dontThrottleNames;
vector<string> parts;
::arg().set("unique-response-db-size", "Size of the DB used to track unique responses in terms of number of cells. Defaults to 67108864")="67108864";
::arg().set("unique-response-pb-tag", "If protobuf is configured, the tag to use for messages containing unique DNS responses. Defaults to 'pdns-udr'")="pdns-udr";
#endif /* NOD_ENABLED */
+
+ ::arg().setSwitch("extended-errors", "If set, send the EDNS Extended Error extension on DNSSEC validation failures")="no";
+
::arg().setCmd("help","Provide a helpful message");
::arg().setCmd("version","Print version string");
::arg().setCmd("config","Output blank configuration");
dnssecinfra.hh dnssecinfra.cc \
dnsseckeeper.hh \
dnswriter.cc dnswriter.hh \
+ ednsextendederror.cc ednsextendederror.hh \
ednsoptions.cc ednsoptions.hh \
ednssubnet.cc ednssubnet.hh \
filterpo.cc filterpo.hh \
So an entry called 'pc' with ``export-etc-hosts-search-suffix='home.com'`` will lead to the generation of 'pc.home.com' within the recursor.
An entry called 'server1.home' will be stored as 'server1.home', regardless of this setting.
+.. _setting-extended-errors:
+
+``extended-errors``
+-------------------
+.. versionadded:: 4.5.0
+
+- Boolean
+- Default: no
+
+If set, the recursor will add an EDNS Extended Error to responses failing DNSSEC validation, explaining the failure.
+
.. _setting-forward-zones:
``forward-zones``
--- /dev/null
+/*
+ * This file is part of PowerDNS or dnsdist.
+ * Copyright -- PowerDNS.COM B.V. and its contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In addition, for the avoidance of any doubt, permission is granted to
+ * link this program with OpenSSL and to (re)distribute the binaries
+ * produced as the result of such linking.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#include "ednsextendederror.hh"
+#include "views.hh"
+
+static bool getEDNSExtendedErrorOptFromString(const pdns_string_view option, EDNSExtendedError& eee)
+{
+ if (option.size() < sizeof(uint16_t)) {
+ return false;
+ }
+ eee.infoCode = static_cast<uint8_t>(option.at(0)) * 256 + static_cast<uint8_t>(option.at(1));
+
+ if (option.size() > sizeof(uint16_t)) {
+ eee.extraText = std::string(&option.at(sizeof(uint16_t)), option.size() - sizeof(uint16_t));
+ }
+
+ return true;
+}
+
+bool getEDNSExtendedErrorOptFromString(const string& option, EDNSExtendedError& eee)
+{
+ return getEDNSExtendedErrorOptFromString(option, eee);
+}
+
+bool getEDNSExtendedErrorOptFromString(const char* option, unsigned int len, EDNSExtendedError& eee)
+{
+ return getEDNSExtendedErrorOptFromString(pdns_string_view(option, len), eee);
+}
+
+string makeEDNSExtendedErrorOptString(const EDNSExtendedError& eee)
+{
+ string ret;
+ ret.reserve(sizeof(uint16_t) + eee.extraText.size());
+ ret.resize(sizeof(uint16_t));
+
+ ret[0] = static_cast<char>(static_cast<uint16_t>(eee.infoCode) / 256);
+ ret[1] = static_cast<char>(static_cast<uint16_t>(eee.infoCode) % 256);
+ ret.append(eee.extraText);
+
+ return ret;
+}
--- /dev/null
+/*
+ * This file is part of PowerDNS or dnsdist.
+ * Copyright -- PowerDNS.COM B.V. and its contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In addition, for the avoidance of any doubt, permission is granted to
+ * link this program with OpenSSL and to (re)distribute the binaries
+ * produced as the result of such linking.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#pragma once
+#include "namespaces.hh"
+
+struct EDNSExtendedError
+{
+ enum class code : uint16_t { Other=0, UnsupportedDNSKEYAlgorithm=1, UnsupportedDSDigestType=2, StaleAnswer=3, ForgedAnswer=4, DNSSECIndeterminate=5, DNSSECBogus=6, SignatureExpired=7, SignatureNotYetValid=8, DNSKEYMissing=9, RRSIGsMissing=10, NoZoneKeyBitSet=11, NSECMissing=12, CachedError=13, NotReady=14, Blocked=15, Censored=16, Filtered=17, Prohibited=18, StaleNXDOMAINAnswer=19, NotAuthoritative=20, NotSupported=21, NoReachableAuthority=22, NetworkError=23, InvalidData=24 };
+ uint16_t infoCode;
+ std::string extraText;
+};
+
+bool getEDNSExtendedErrorOptFromString(const char* option, unsigned int len, EDNSExtendedError& eee);
+bool getEDNSExtendedErrorOptFromString(const string& option, EDNSExtendedError& eee);
+string makeEDNSExtendedErrorOptString(const EDNSExtendedError& eee);