#include "dnsdist.hh"
#include "dnsdist-carbon.hh"
+#include "dnsdist-concurrent-connections.hh"
#include "dnsdist-console.hh"
#include "dnsdist-dynblocks.hh"
#include "dnsdist-discovery.hh"
luaCtx.writeFunction("setMaxTCPConnectionsPerClient", [](uint64_t max) {
if (!g_configurationDone) {
- g_maxTCPConnectionsPerClient = max;
+ dnsdist::IncomingConcurrentTCPConnectionsManager::setMaxTCPConnectionsPerClient(max);
}
else {
g_outputBuffer = "The maximum number of TCP connection per client cannot be altered at runtime!\n";
#include <queue>
#include "dnsdist.hh"
+#include "dnsdist-concurrent-connections.hh"
#include "dnsdist-ecs.hh"
#include "dnsdist-proxy-protocol.hh"
#include "dnsdist-rings.hh"
Let's start naively.
*/
-static LockGuarded<std::map<ComboAddress,size_t,ComboAddress::addressOnlyLessThan>> s_tcpClientsCount;
-
size_t g_maxTCPQueriesPerConn{0};
size_t g_maxTCPConnectionDuration{0};
-size_t g_maxTCPConnectionsPerClient{0};
+
#ifdef __linux__
// On Linux this gives us 128k pending queries (default is 8192 queries),
// which should be enough to deal with huge spikes
int g_tcpSendTimeout{2};
std::atomic<uint64_t> g_tcpStatesDumpRequested{0};
-static void decrementTCPClientCount(const ComboAddress& client)
-{
- if (g_maxTCPConnectionsPerClient) {
- auto tcpClientsCount = s_tcpClientsCount.lock();
- tcpClientsCount->at(client)--;
- if (tcpClientsCount->at(client) == 0) {
- tcpClientsCount->erase(client);
- }
- }
-}
+LockGuarded<std::map<ComboAddress, size_t, ComboAddress::addressOnlyLessThan>> dnsdist::IncomingConcurrentTCPConnectionsManager::s_tcpClientsConcurrentConnectionsCount;
+size_t dnsdist::IncomingConcurrentTCPConnectionsManager::s_maxTCPConnectionsPerClient = 0;
IncomingTCPConnectionState::~IncomingTCPConnectionState()
{
- decrementTCPClientCount(d_ci.remote);
+ dnsdist::IncomingConcurrentTCPConnectionsManager::accountClosedTCPConnection(d_ci.remote);
if (d_ci.cs != nullptr) {
struct timeval now;
return;
}
- if (g_maxTCPConnectionsPerClient) {
- auto tcpClientsCount = s_tcpClientsCount.lock();
-
- if ((*tcpClientsCount)[remote] >= g_maxTCPConnectionsPerClient) {
- vinfolog("Dropping TCP connection from %s because we have too many from this client already", remote.toStringWithPort());
- return;
- }
- (*tcpClientsCount)[remote]++;
- tcpClientCountIncremented = true;
+ if (!dnsdist::IncomingConcurrentTCPConnectionsManager::accountNewTCPConnection(remote)) {
+ vinfolog("Dropping TCP connection from %s because we have too many from this client already", remote.toStringWithPort());
+ return;
}
+ tcpClientCountIncremented = true;
vinfolog("Got TCP connection from %s", remote.toStringWithPort());
if (threadData == nullptr) {
if (!g_tcpclientthreads->passConnectionToThread(std::make_unique<ConnectionInfo>(std::move(ci)))) {
if (tcpClientCountIncremented) {
- decrementTCPClientCount(remote);
+ dnsdist::IncomingConcurrentTCPConnectionsManager::accountClosedTCPConnection(remote);
}
}
}
catch (const std::exception& e) {
errlog("While reading a TCP question: %s", e.what());
if (tcpClientCountIncremented) {
- decrementTCPClientCount(remote);
+ dnsdist::IncomingConcurrentTCPConnectionsManager::accountClosedTCPConnection(remote);
}
}
catch (...){}
extern uint64_t g_maxTCPQueuedConnections;
extern size_t g_maxTCPQueriesPerConn;
extern size_t g_maxTCPConnectionDuration;
-extern size_t g_maxTCPConnectionsPerClient;
extern size_t g_tcpInternalPipeBufferSize;
extern pdns::stat16_t g_cacheCleaningDelay;
extern pdns::stat16_t g_cacheCleaningPercentage;
dnsdist-backend.cc \
dnsdist-cache.cc dnsdist-cache.hh \
dnsdist-carbon.cc dnsdist-carbon.hh \
+ dnsdist-concurrent-connections.hh \
dnsdist-console.cc dnsdist-console.hh \
dnsdist-discovery.cc dnsdist-discovery.hh \
dnsdist-dnscrypt.cc \
dnsdist-async.cc dnsdist-async.hh \
dnsdist-backend.cc \
dnsdist-cache.cc dnsdist-cache.hh \
+ dnsdist-concurrent-connections.hh \
dnsdist-dnsparser.cc dnsdist-dnsparser.hh \
dnsdist-downstream-connection.hh \
dnsdist-dynblocks.cc dnsdist-dynblocks.hh \
--- /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 <map>
+#include "iputils.hh"
+#include "lock.hh"
+
+namespace dnsdist
+{
+class IncomingConcurrentTCPConnectionsManager
+{
+public:
+ static bool accountNewTCPConnection(const ComboAddress& from)
+ {
+ if (s_maxTCPConnectionsPerClient == 0) {
+ return true;
+ }
+ auto db = s_tcpClientsConcurrentConnectionsCount.lock();
+ auto& count = (*db)[from];
+ if (count >= s_maxTCPConnectionsPerClient) {
+ return false;
+ }
+ ++count;
+ return true;
+ }
+
+ static void accountClosedTCPConnection(const ComboAddress& from)
+ {
+ if (s_maxTCPConnectionsPerClient == 0) {
+ return;
+ }
+ auto db = s_tcpClientsConcurrentConnectionsCount.lock();
+ auto& count = db->at(from);
+ count--;
+ if (count == 0) {
+ db->erase(from);
+ }
+ }
+
+ static void setMaxTCPConnectionsPerClient(size_t max)
+ {
+ s_maxTCPConnectionsPerClient = max;
+ }
+
+private:
+ static LockGuarded<std::map<ComboAddress, size_t, ComboAddress::addressOnlyLessThan>> s_tcpClientsConcurrentConnectionsCount;
+ static size_t s_maxTCPConnectionsPerClient;
+};
+
+}
#include "misc.hh"
#include "dns.hh"
#include "dolog.hh"
+#include "dnsdist-concurrent-connections.hh"
#include "dnsdist-ecs.hh"
#include "dnsdist-proxy-protocol.hh"
#include "dnsdist-rules.hh"
}
t_conns.erase(conn->d_desc);
+ dnsdist::IncomingConcurrentTCPConnectionsManager::accountClosedTCPConnection(conn->d_remote);
}
}
++dsc->cs->tlsResumptions;
}
- if (h2o_socket_getpeername(sock, reinterpret_cast<struct sockaddr*>(&conn.d_remote)) == 0) {
- /* getpeername failed, likely because the connection has already been closed,
- but anyway that means we can't get the remote address, which could allow an ACL bypass */
- h2o_send_error_500(req, getReasonFromStatusCode(500).c_str(), "Internal Server Error - Unable to get remote address", 0);
- return 0;
- }
-
h2o_socket_getsockname(sock, reinterpret_cast<struct sockaddr*>(&conn.d_local));
}
return;
}
+ ComboAddress remote;
+ if (h2o_socket_getpeername(sock, reinterpret_cast<struct sockaddr*>(&remote)) == 0) {
+ vinfolog("Dropping DoH connection because we could not retrieve the remote host");
+ h2o_socket_close(sock);
+ return;
+ }
+
+ if (!dnsdist::IncomingConcurrentTCPConnectionsManager::accountNewTCPConnection(remote)) {
+ vinfolog("Dropping DoH connection from %s because we have too many from this client already", remote.toStringWithPort());
+ h2o_socket_close(sock);
+ return;
+ }
+
auto concurrentConnections = ++dsc->cs->tcpCurrentConnections;
if (dsc->cs->d_tcpConcurrentConnectionsLimit > 0 && concurrentConnections > dsc->cs->d_tcpConcurrentConnectionsLimit) {
--dsc->cs->tcpCurrentConnections;
conn.d_nbQueries = 0;
conn.d_acceptCtx = std::atomic_load_explicit(&dsc->accept_ctx, std::memory_order_acquire);
conn.d_desc = descriptor;
+ conn.d_remote = remote;
sock->on_close.cb = on_socketclose;
sock->on_close.data = &conn;