--- /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 "ednspadding.hh"
+
+std::string makeEDNSPaddingOptString(size_t bytes)
+{
+ std::string ret;
+ ret.resize(bytes, '0');
+ 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 <string>
+
+std::string makeEDNSPaddingOptString(size_t bytes);
#include "rec-lua-conf.hh"
#include "ednsoptions.hh"
#include "ednsextendederror.hh"
+#include "ednspadding.hh"
#include "gettime.hh"
#include "proxy-protocol.hh"
#include "pubsuffix.hh"
typedef vector<int> tcpListenSockets_t;
typedef map<int, ComboAddress> listenSocketsAddresses_t; // is shared across all threads right now
+enum class PaddingMode { Always, PaddedQueries };
static listenSocketsAddresses_t g_listenSocketsAddresses; // is shared across all threads right now
static set<int> g_fromtosockets; // listen sockets that use 'sendfromto()' mechanism (without actually using sendfromto())
static std::shared_ptr<NetmaskGroup> g_initialAllowFrom; // new thread needs to be setup with this
static NetmaskGroup g_XPFAcl;
static NetmaskGroup g_proxyProtocolACL;
+static NetmaskGroup g_paddingFrom;
static boost::optional<ComboAddress> g_dns64Prefix{boost::none};
static DNSName g_dns64PrefixReverse;
static size_t g_proxyProtocolMaximumSize;
static unsigned int g_maxMThreads;
static unsigned int g_numDistributorThreads;
static unsigned int g_numWorkerThreads;
+static unsigned int g_paddingTag;
static int g_tcpTimeout;
static uint16_t g_udpTruncationThreshold;
static uint16_t g_xpfRRCode{0};
+static PaddingMode g_paddingMode;
static std::atomic<bool> statsWanted;
static std::atomic<bool> g_quiet;
static bool g_logCommonErrors;
std::vector<pair<uint16_t, string> > ednsOpts;
bool variableAnswer = dc->d_variable;
bool haveEDNS=false;
+ bool paddingAllowed = g_paddingFrom.match(dc->d_remote);
+ bool padResponse = paddingAllowed ? (g_paddingMode == PaddingMode::Always) : false;
#ifdef NOD_ENABLED
bool hasUDR = false;
#endif /* NOD_ENABLED */
variableAnswer = true; // Can't packetcache an answer with NSID
maxanswersize -= EDNSOptionCodeSize + EDNSOptionLengthSize + mode_server_id.size();
}
+ } else if (paddingAllowed && padResponse == false && g_paddingMode == PaddingMode::PaddedQueries && o.first == EDNSOptionCode::PADDING) {
+ /* we should only pad on 'secure' cases to limit amplification:
+ - over TCP ;
+ - from 'known-good' sources (dnsdist for example).
+ We should also support a strict mode (only pad if the query has a padding option)
+ and a relaxed mode (pad if the client supports EDNS).
+ The block-size should be configurable as well.
+ */
+ padResponse = true;
+ maxanswersize -= 4;
}
}
}
+
+ if (padResponse && dc->d_tag == 0) {
+ dc->d_tag = g_paddingTag;
+ }
+
/* perhaps there was no EDNS or no ECS but by now we looked */
dc->d_ecsParsed = true;
vector<DNSRecord> ret;
}
}
+ if (padResponse) {
+ size_t currentSize = pw.getSizeWithOpts(returnedEdnsOptions);
+ if (currentSize < (static_cast<size_t>(maxanswersize) - 4)) {
+ size_t remaining = static_cast<size_t>(maxanswersize) - currentSize;
+ /* from rfc8647, "4.1. Recommended Strategy: Block-Length Padding":
+ If a server receives a query that includes the EDNS(0) "Padding"
+ option, it MUST pad the corresponding response (see Section 4 of
+ RFC 7830) and SHOULD pad the corresponding response to a
+ multiple of 468 octets (see below).
+ */
+ static const size_t blockSize = 468;
+ size_t modulo = (currentSize + 4) % blockSize;
+ size_t padSize = 0;
+ if (modulo > 0) {
+ padSize = std::min(blockSize - modulo, remaining);
+ }
+ returnedEdnsOptions.push_back(make_pair(EDNSOptionCode::PADDING, makeEDNSPaddingOptString(padSize)));
+ }
+ }
+
if (haveEDNS) {
auto state = sr.getValidationState();
if (dc->d_extendedErrorCode || (s_addExtendedResolutionDNSErrors && vStateIsBogus(state))) {
}
}
+ if (dc->d_tag == 0 && g_paddingFrom.match(dc->d_remote)) {
+ dc->d_tag = g_paddingTag;
+ }
+
const struct dnsheader* dh = reinterpret_cast<const struct dnsheader*>(&conn->data[0]);
if (t_protobufServers || t_outgoingProtobufServers) {
}
RecursorPacketCache::OptPBData pbData{boost::none};
-
if (t_protobufServers) {
if (logQuery && !(luaconfsLocal->protobufExportConfig.taggedOnly && policyTags.empty())) {
protobufLogQuery(luaconfsLocal, uniqueId, source, destination, ednssubnet.source, false, dh->id, question.size(), qname, qtype, qclass, policyTags, requestorId, deviceId, deviceName);
}
}
+ if (ctag == 0 && g_paddingFrom.match(fromaddr)) {
+ ctag = g_paddingTag;
+ }
/* It might seem like a good idea to skip the packet cache lookup if we know that the answer is not cacheable,
but it means that the hash would not be computed. If some script decides at a later time to mark back the answer
as cacheable we would cache it with a wrong tag, so better safe than sorry. */
g_lowercaseOutgoing = ::arg().mustDo("lowercase-outgoing");
+ g_paddingFrom.toMasks(::arg()["edns-padding-from"]);
+ if (::arg()["edns-padding-mode"] == "always") {
+ g_paddingMode = PaddingMode::Always;
+ }
+ else if (::arg()["edns-padding-mode"] == "padded-queries-only") {
+ g_paddingMode = PaddingMode::PaddedQueries;
+ }
+ else {
+ g_log << Logger::Error << "Unknown edns-padding-mode: " << ::arg()["edns-padding-mode"] << endl;
+ exit(1);
+ }
+ g_paddingTag = ::arg().asNum("edns-padding-tag");
+
g_numDistributorThreads = ::arg().asNum("distributor-threads");
g_numWorkerThreads = ::arg().asNum("threads");
if (g_numWorkerThreads < 1) {
::arg().setSwitch("aggressive-nsec-cache-size", "The number of records to cache in the aggressive cache. If set to a value greater than 0, and DNSSEC validation is enabled, the recursor will cache NSEC and NSEC3 records to generate negative answers, as defined in rfc8198")="100000";
+ ::arg().set("edns-padding-from", "Sources (proxy IP in case of XPF or proxy-protocol presence, client IP otherwise) for which EDNS padding will be enabled in responses")="";
+ ::arg().set("edns-padding-mode", "Whether to add EDNS padding to all responses ('always') or only to the ones to padded queries ('padded-queries-only')")="padded-queries-only";
+ ::arg().set("edns-padding-tag", "Packetcache tag associated to responses sent with EDNS padding, to prevent sending these to non-whitelisted clients.")="7830";
+
::arg().setCmd("help","Provide a helpful message");
::arg().setCmd("version","Print version string");
::arg().setCmd("config","Output blank configuration");