#endif
}
-/* Parts of this code come from arc4random_uniform */
uint32_t dns_random(uint32_t upper_bound) {
if (chosen_rng == RNG_UNINITIALIZED)
dns_random_setup();
- unsigned int min;
if (upper_bound < 2)
return 0;
- /* To avoid "modulo bias" for some methods, calculate
- minimum acceptable value for random number to improve
- uniformity.
- On applicable rngs, we loop until the rng spews out
- value larger than min, and then take modulo out of that.
- */
-#if (ULONG_MAX > 0xffffffffUL)
- min = 0x100000000UL % upper_bound;
-#else
- /* Calculate (2**32 % upper_bound) avoiding 64-bit math */
- if (upper_bound > 0x80000000)
- min = 1 + ~upper_bound; /* 2**32 - upper_bound */
- else {
- /* (2**32 - (x * 2)) % x == 2**32 % x when x <= 2**31 */
- min = ((0xffffffff - (upper_bound * 2)) + 1) % upper_bound;
- }
-#endif
+ unsigned int min = pdns::random_minimum_acceptable_value(upper_bound);
switch(chosen_rng) {
case RNG_UNINITIALIZED:
#pragma once
#include <cstdint>
#include <limits>
+#include <string>
void dns_random_init(const std::string& data = "", bool force_reinit = false);
uint32_t dns_random(uint32_t n);
return dns_random(std::numeric_limits<result_type>::max());
}
};
+
+ /* minimum value that a PRNG should return for this upper bound to avoid a modulo bias */
+ inline unsigned int random_minimum_acceptable_value(uint32_t upper_bound)
+ {
+ /* Parts of this code come from arc4random_uniform */
+ /* To avoid "modulo bias" for some methods, calculate
+ minimum acceptable value for random number to improve
+ uniformity.
+
+ On applicable rngs, we loop until the rng spews out
+ value larger than min, and then take modulo out of that.
+ */
+ unsigned int min;
+#if (ULONG_MAX > 0xffffffffUL)
+ min = 0x100000000UL % upper_bound;
+#else
+ /* Calculate (2**32 % upper_bound) avoiding 64-bit math */
+ if (upper_bound > 0x80000000)
+ min = 1 + ~upper_bound; /* 2**32 - upper_bound */
+ else {
+ /* (2**32 - (x * 2)) % x == 2**32 % x when x <= 2**31 */
+ min = ((0xffffffff - (upper_bound * 2)) + 1) % upper_bound;
+ }
+#endif
+ return min;
+ }
}
sentTime(true), tempFailureTTL(boost::none) { origDest.sin4.sin_family = 0; }
IDState(const IDState& orig) = delete;
IDState(IDState&& rhs) :
- subnet(rhs.subnet), origRemote(rhs.origRemote), origDest(rhs.origDest), hopRemote(rhs.hopRemote), hopLocal(rhs.hopLocal), qname(std::move(rhs.qname)), sentTime(rhs.sentTime), packetCache(std::move(rhs.packetCache)), dnsCryptQuery(std::move(rhs.dnsCryptQuery)), qTag(std::move(rhs.qTag)), tempFailureTTL(rhs.tempFailureTTL), cs(rhs.cs), du(std::move(rhs.du)), cacheKey(rhs.cacheKey), cacheKeyNoECS(rhs.cacheKeyNoECS), cacheKeyUDP(rhs.cacheKeyUDP), origFD(rhs.origFD), delayMsec(rhs.delayMsec), qtype(rhs.qtype), qclass(rhs.qclass), origID(rhs.origID), origFlags(rhs.origFlags), cacheFlags(rhs.cacheFlags), protocol(rhs.protocol), ednsAdded(rhs.ednsAdded), ecsAdded(rhs.ecsAdded), skipCache(rhs.skipCache), destHarvested(rhs.destHarvested), dnssecOK(rhs.dnssecOK), useZeroScope(rhs.useZeroScope)
+ subnet(rhs.subnet), origRemote(rhs.origRemote), origDest(rhs.origDest), hopRemote(rhs.hopRemote), hopLocal(rhs.hopLocal), qname(std::move(rhs.qname)), sentTime(rhs.sentTime), packetCache(std::move(rhs.packetCache)), dnsCryptQuery(std::move(rhs.dnsCryptQuery)), qTag(std::move(rhs.qTag)), tempFailureTTL(rhs.tempFailureTTL), cs(rhs.cs), du(std::move(rhs.du)), cacheKey(rhs.cacheKey), cacheKeyNoECS(rhs.cacheKeyNoECS), cacheKeyUDP(rhs.cacheKeyUDP), origFD(rhs.origFD), backendFD(rhs.backendFD), delayMsec(rhs.delayMsec), qtype(rhs.qtype), qclass(rhs.qclass), origID(rhs.origID), origFlags(rhs.origFlags), cacheFlags(rhs.cacheFlags), protocol(rhs.protocol), ednsAdded(rhs.ednsAdded), ecsAdded(rhs.ecsAdded), skipCache(rhs.skipCache), destHarvested(rhs.destHarvested), dnssecOK(rhs.dnssecOK), useZeroScope(rhs.useZeroScope)
{
if (rhs.isInUse()) {
throw std::runtime_error("Trying to move an in-use IDState");
cacheKeyNoECS = rhs.cacheKeyNoECS;
cacheKeyUDP = rhs.cacheKeyUDP;
origFD = rhs.origFD;
+ backendFD = rhs.backendFD;
delayMsec = rhs.delayMsec;
#ifdef __SANITIZE_THREAD__
age.store(rhs.age.load());
// DoH-only */
uint32_t cacheKeyUDP{0}; // 4
int origFD{-1}; // 4
+ int backendFD{-1}; // 4
int delayMsec{0};
#ifdef __SANITIZE_THREAD__
std::atomic<uint16_t> age{0};
g_socketUDPRecvBuffer = recv;
});
+ luaCtx.writeFunction("setRandomizedOutgoingSockets", [](bool randomized) {
+ DownstreamState::s_randomizeSockets = randomized;
+ });
+
#if defined(HAVE_LIBSSL)
luaCtx.writeFunction("loadTLSEngine", [client](const std::string& engineName, boost::optional<std::string> defaultString) {
if (client) {
#include "dnsdist-lua.hh"
#include "dnsdist-nghttp2.hh"
#include "dnsdist-proxy-protocol.hh"
+#include "dnsdist-random.hh"
#include "dnsdist-rings.hh"
#include "dnsdist-secpoll.hh"
#include "dnsdist-tcp.hh"
return true;
}
-int pickBackendSocketForSending(std::shared_ptr<DownstreamState>& state)
-{
- return state->sockets[state->socketsOffset++ % state->sockets.size()];
-}
-
-static void pickBackendSocketsReadyForReceiving(const std::shared_ptr<DownstreamState>& state, std::vector<int>& ready)
-{
- ready.clear();
-
- if (state->sockets.size() == 1) {
- ready.push_back(state->sockets[0]);
- return ;
- }
-
- (*state->mplexer.lock())->getAvailableFDs(ready, 1000);
-}
-
void handleResponseSent(const IDState& ids, double udiff, const ComboAddress& client, const ComboAddress& backend, unsigned int size, const dnsheader& cleartextDH, dnsdist::Protocol protocol)
{
struct timespec ts;
for(;;) {
try {
- pickBackendSocketsReadyForReceiving(dss, sockets);
+ dss->pickSocketsReadyForReceiving(sockets);
if (dss->isStopped()) {
break;
}
int origFD = ids->origFD;
unsigned int qnameWireLength = 0;
- if (!responseContentMatches(response, ids->qname, ids->qtype, ids->qclass, dss->remote, qnameWireLength)) {
+ if (fd != ids->backendFD || !responseContentMatches(response, ids->qname, ids->qtype, ids->qclass, dss->remote, qnameWireLength)) {
continue;
}
addProxyProtocol(dq);
}
- int fd = pickBackendSocketForSending(ss);
+ int fd = ss->pickSocketForSending();
+ ids->backendFD = fd;
ssize_t ret = udpClientSendRequestToBackend(ss, fd, query);
if(ret < 0) {
}
}
-
-uint16_t getRandomDNSID()
-{
-#ifdef HAVE_LIBSODIUM
- return randombytes_uniform(65536);
-#else
- return (random() % 65536);
-#endif
-}
-
boost::optional<uint64_t> g_maxTCPClientThreads{boost::none};
pdns::stat16_t g_cacheCleaningDelay{60};
pdns::stat16_t g_cacheCleaningPercentage{100};
cerr<<"Unable to initialize crypto library"<<endl;
exit(EXIT_FAILURE);
}
- g_hashperturb=randombytes_uniform(0xffffffff);
- srandom(randombytes_uniform(0xffffffff));
-#else
- {
- struct timeval tv;
- gettimeofday(&tv, 0);
- srandom(tv.tv_sec ^ tv.tv_usec ^ getpid());
- g_hashperturb=random();
- }
-
#endif
+ dnsdist::initRandom();
+ g_hashperturb = dnsdist::getRandomValue(0xffffffff);
+
ComboAddress clientAddress = ComboAddress();
g_cmdLine.config=SYSCONFDIR "/dnsdist.conf";
struct option longopts[]={
struct DownstreamState
{
- typedef std::function<std::tuple<DNSName, uint16_t, uint16_t>(const DNSName&, uint16_t, uint16_t, dnsheader*)> checkfunc_t;
+ typedef std::function<std::tuple<DNSName, uint16_t, uint16_t>(const DNSName&, uint16_t, uint16_t, dnsheader*)> checkfunc_t;
DownstreamState(const ComboAddress& remote_, const ComboAddress& sourceAddr_, unsigned int sourceItf, const std::string& sourceItfName);
DownstreamState(const ComboAddress& remote_): DownstreamState(remote_, ComboAddress(), 0, std::string()) {}
}
bool passCrossProtocolQuery(std::unique_ptr<CrossProtocolQuery>&& cpq);
+ int pickSocketForSending();
+ void pickSocketsReadyForReceiving(std::vector<int>& ready);
+
dnsdist::Protocol getProtocol() const
{
if (isDoH()) {
}
return dnsdist::Protocol::DoUDP;
}
+
+ static bool s_randomizeSockets;
};
using servers_t =vector<std::shared_ptr<DownstreamState>>;
int handleDNSCryptQuery(PacketBuffer& packet, DNSCryptQuery& query, bool tcp, time_t now, PacketBuffer& response);
bool checkDNSCryptQuery(const ClientState& cs, PacketBuffer& query, std::unique_ptr<DNSCryptQuery>& dnsCryptQuery, time_t now, bool tcp);
-uint16_t getRandomDNSID();
-
#include "dnsdist-snmp.hh"
extern bool g_snmpEnabled;
DNSResponse makeDNSResponseFromIDState(IDState& ids, PacketBuffer& data);
void setIDStateFromDNSQuestion(IDState& ids, DNSQuestion& dq, DNSName&& qname);
-int pickBackendSocketForSending(std::shared_ptr<DownstreamState>& state);
ssize_t udpClientSendRequestToBackend(const std::shared_ptr<DownstreamState>& ss, const int sd, const PacketBuffer& request, bool healthCheck = false);
void handleResponseSent(const IDState& ids, double udiff, const ComboAddress& client, const ComboAddress& backend, unsigned int size, const dnsheader& cleartextDH, dnsdist::Protocol protocol);
connection-management.hh \
credentials.cc credentials.hh \
dns.cc dns.hh \
+ dns_random.hh \
dnscrypt.cc dnscrypt.hh \
dnsdist-backend.cc \
dnsdist-cache.cc dnsdist-cache.hh \
dnsdist-protobuf.cc dnsdist-protobuf.hh \
dnsdist-protocols.cc dnsdist-protocols.hh \
dnsdist-proxy-protocol.cc dnsdist-proxy-protocol.hh \
+ dnsdist-random.cc dnsdist-random.hh \
dnsdist-rings.cc dnsdist-rings.hh \
dnsdist-rules.cc dnsdist-rules.hh \
dnsdist-secpoll.cc dnsdist-secpoll.hh \
dnsdist-nghttp2.cc dnsdist-nghttp2.hh \
dnsdist-protocols.cc dnsdist-protocols.hh \
dnsdist-proxy-protocol.cc dnsdist-proxy-protocol.hh \
+ dnsdist-random.cc dnsdist-random.hh \
dnsdist-rings.cc dnsdist-rings.hh \
dnsdist-rules.cc dnsdist-rules.hh \
dnsdist-session-cache.cc dnsdist-session-cache.hh \
--- /dev/null
+../dns_random.hh
\ No newline at end of file
#include "dnsdist.hh"
#include "dnsdist-nghttp2.hh"
+#include "dnsdist-random.hh"
#include "dnsdist-tcp.hh"
#include "dolog.hh"
}
}
+int DownstreamState::pickSocketForSending()
+{
+ size_t numberOfSockets = sockets.size();
+ if (numberOfSockets == 1) {
+ return sockets[0];
+ }
+
+ size_t idx;
+ if (s_randomizeSockets) {
+ idx = dnsdist::getRandomValue(numberOfSockets);
+ }
+ else {
+ idx = socketsOffset++;
+ }
+
+ return sockets[idx % numberOfSockets];
+}
+
+void DownstreamState::pickSocketsReadyForReceiving(std::vector<int>& ready)
+{
+ ready.clear();
+
+ if (sockets.size() == 1) {
+ ready.push_back(sockets[0]);
+ return ;
+ }
+
+ (*mplexer.lock())->getAvailableFDs(ready, 1000);
+}
+
+bool DownstreamState::s_randomizeSockets{false};
+
size_t ServerPool::countServers(bool upOnly)
{
size_t count = 0;
#include "tcpiohandler-mplexer.hh"
#include "dnswriter.hh"
#include "dolog.hh"
+#include "dnsdist-random.hh"
#include "dnsdist-tcp.hh"
#include "dnsdist-nghttp2.hh"
#include "dnsdist-session-cache.hh"
{
try
{
- uint16_t queryID = getRandomDNSID();
+ uint16_t queryID = dnsdist::getRandomDNSID();
DNSName checkName = ds->checkName;
uint16_t checkType = ds->checkType.getCode();
uint16_t checkClass = ds->checkClass;
--- /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 "config.h"
+
+#include <stdexcept>
+#include <sys/time.h>
+#include <unistd.h>
+#ifdef HAVE_LIBSODIUM
+#include <sodium.h>
+#endif /* HAVE_LIBSODIUM */
+#ifdef HAVE_RAND_BYTES
+#include <openssl/rand.h>
+#endif /* HAVE_RAND_BYTES */
+
+#include "dnsdist-random.hh"
+#include "dns_random.hh"
+
+namespace dnsdist
+{
+void initRandom()
+{
+#ifdef HAVE_LIBSODIUM
+ srandom(randombytes_uniform(0xffffffff));
+#else
+ {
+ auto getSeed = []() {
+#ifdef HAVE_RAND_BYTES
+ unsigned int seed;
+ if (RAND_bytes(reinterpret_cast<unsigned char*>(&seed), sizeof(seed)) == 1) {
+ return seed;
+ }
+#endif /* HAVE_RAND_BYTES */
+ struct timeval tv;
+ gettimeofday(&tv, 0);
+ return static_cast<unsigned int>(tv.tv_sec ^ tv.tv_usec ^ getpid());
+ };
+
+ srandom(getSeed());
+ }
+#endif
+}
+
+uint32_t getRandomValue(uint32_t upperBound)
+{
+#ifdef HAVE_LIBSODIUM
+ return randombytes_uniform(upperBound);
+#else /* HAVE_LIBSODIUM */
+ uint32_t result;
+ unsigned int min = pdns::random_minimum_acceptable_value(upperBound);
+#ifdef HAVE_RAND_BYTES
+ do {
+ if (RAND_bytes(reinterpret_cast<unsigned char*>(&result), sizeof(result)) != 1) {
+ throw std::runtime_error("Error getting a random value via RAND_bytes");
+ }
+ } while (result < min);
+
+ return result % upperBound;
+#endif /* HAVE_RAND_BYTES */
+ do {
+ result = random();
+ } while (result < min);
+
+ return result % upperBound;
+#endif /* HAVE_LIBSODIUM */
+}
+
+uint16_t getRandomDNSID()
+{
+ return getRandomValue(65536);
+}
+}
--- /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 <cstdint>
+
+namespace dnsdist
+{
+void initRandom();
+uint32_t getRandomValue(uint32_t upperBound);
+uint16_t getRandomDNSID();
+}
#include "sstuff.hh"
#include "dnsdist.hh"
+#include "dnsdist-random.hh"
#ifndef PACKAGEVERSION
#define PACKAGEVERSION PACKAGE_VERSION
const DNSName& sentName = DNSName(queriedName);
std::vector<uint8_t> packet;
DNSPacketWriter pw(packet, sentName, QType::TXT);
- pw.getHeader()->id = getRandomDNSID();
+ pw.getHeader()->id = dnsdist::getRandomDNSID();
pw.getHeader()->rd = 1;
const auto& resolversForStub = getResolvers("/etc/resolv.conf");
}
}
- int fd = pickBackendSocketForSending(du->downstream);
+ int fd = du->downstream->pickSocketForSending();
+ ids->backendFD = fd;
try {
/* you can't touch du after this line, unless the call returned a non-negative value,
because it might already have been freed */